Compare commits

..

1797 Commits

Author SHA1 Message Date
Girish Ramakrishnan 282040ed1b gcs: use delete concurrency 2021-02-01 14:23:15 -08:00
Girish Ramakrishnan bcd04715c0 updater: set the backup memory limit 2021-02-01 14:07:23 -08:00
Johannes Zellner 14b2fa55c3 Update sftp 3.1.0 addon image 2021-02-01 19:20:58 +01:00
Johannes Zellner 04e103a32d Do not bump infra version 2021-02-01 19:06:13 +01:00
Johannes Zellner 0b0c02e421 Update sftp image for copy function 2021-02-01 16:13:46 +01:00
Girish Ramakrishnan 196a5cfb42 Add missing require 2021-01-31 20:47:33 -08:00
Girish Ramakrishnan fc408b8288 Fix app auto-update breakage 2021-01-31 20:46:55 -08:00
Girish Ramakrishnan e2c342f242 apptaskmanager: Fix crash 2021-01-30 21:16:41 -08:00
Girish Ramakrishnan 19fcabd32b mail: data.headers is now headers 2021-01-29 00:02:03 -08:00
Girish Ramakrishnan a842d77b6d Fix SOGo login
listAllMailboxes query was mangled
2021-01-28 22:21:44 -08:00
Girish Ramakrishnan ef68cb70c0 email autoconfig 2021-01-28 16:58:37 -08:00
Girish Ramakrishnan adfb506af4 Fix disk usage graphs 2021-01-27 21:48:06 -08:00
Girish Ramakrishnan 1d188297f9 6.1.1 changes 2021-01-27 13:10:40 -08:00
Girish Ramakrishnan 141a32315f ignore any applyServiceConfig failures when starting services 2021-01-27 11:33:27 -08:00
Girish Ramakrishnan 8f7b224846 proxyauth: make auth error handler return 401 for docker client 2021-01-27 00:33:27 -08:00
Girish Ramakrishnan 4610e05ca1 Fix well-known migration 2021-01-26 21:10:06 -08:00
Johannes Zellner cc4407a438 adminMaxCount is not a feature for now, since we have roles feature 2021-01-25 19:14:32 +01:00
Girish Ramakrishnan 5d9568eb91 Fix typo 2021-01-22 11:24:24 -08:00
Johannes Zellner a9f52ba305 Ensure to rebuild reverse proxy config if http port changes on update 2021-01-22 11:25:32 +01:00
Girish Ramakrishnan 9f9575f46a Fixes to service configuration
restart service does not rebuild automatically, we should add a route
for that. we need to figure where to scale services etc if we randomly
create containers like that.
2021-01-21 17:41:22 -08:00
Girish Ramakrishnan 47a598a494 rename getService to getServiceStatus 2021-01-21 12:40:41 -08:00
Girish Ramakrishnan d294dea84d rename getServices to getServiceIds 2021-01-21 12:38:12 -08:00
Girish Ramakrishnan 304fe45ee8 getServicesConfig -> getServiceConfig
it gets setting of a single service. the settings API returns multiple
ones, so it makes sense to call that one getServicesConfig
2021-01-21 12:22:06 -08:00
Girish Ramakrishnan 0edb673dc6 rename platform config to services config 2021-01-21 12:19:57 -08:00
Girish Ramakrishnan cd1b46848e Fix bug where graphite and sftp are not incrementally upgraded 2021-01-21 12:00:23 -08:00
Girish Ramakrishnan 6bd87485c6 rename addons.js to services.js
services is the named container (services view)
addons is more like a heroku concept
2021-01-21 11:31:35 -08:00
Girish Ramakrishnan d5952fafc3 Update changes 2021-01-20 20:32:22 -08:00
Girish Ramakrishnan 7660e90d51 read ratio from swap-ratio 2021-01-20 20:20:00 -08:00
Girish Ramakrishnan 4d482d11ee add apps.getMemoryLimit 2021-01-20 19:16:21 -08:00
Girish Ramakrishnan a14dbbe77a refactor into docker.update 2021-01-20 18:58:23 -08:00
Girish Ramakrishnan 0d535d2d5c allocate swap size for containers based on system ratio 2021-01-20 18:41:51 -08:00
Girish Ramakrishnan 7b24239d38 update the service config in addons code 2021-01-20 11:10:50 -08:00
Girish Ramakrishnan 10d7c47576 Fix typo 2021-01-19 19:58:44 -08:00
Girish Ramakrishnan 025eb18411 Use a single memoryLimit instead of memory and memorySwap
We will make the percent allocation dynamic depending on the system.

When we have servers with a large amount of RAM but little swap, we
seem to use a lot of swap because of 50% allocation strategy. In such
systems, we run out of swap and thus have OOM errors even though there
is a lot of RAM available!
2021-01-19 19:43:41 -08:00
Girish Ramakrishnan 24db6630ee platform config settings route is obsolete (now under services) 2021-01-19 19:35:06 -08:00
Girish Ramakrishnan 0930683366 Fix failing tests 2021-01-19 19:35:06 -08:00
Girish Ramakrishnan 67bdf47ef6 rename hostname to vhost to make the code less magical 2021-01-19 14:09:31 -08:00
Girish Ramakrishnan de869b90ee replace * in alias domain with _ for better filenames
this is similar to what we do for cert filenames
2021-01-19 13:36:31 -08:00
Girish Ramakrishnan 9e2f52caef Add changes 2021-01-19 08:51:20 -08:00
Johannes Zellner b06432824c Add netcup dns provider
Fixes #763
2021-01-19 16:17:10 +01:00
Girish Ramakrishnan 07642f0c56 make multiDomain a boolean 2021-01-18 23:01:39 -08:00
Girish Ramakrishnan f17899d804 allow wilcard in alias domains 2021-01-18 22:59:31 -08:00
Girish Ramakrishnan 88cd857f97 rename main to primary 2021-01-18 22:31:10 -08:00
Girish Ramakrishnan 195fb198dd implement domain aliases 2021-01-18 17:34:39 -08:00
Girish Ramakrishnan ad2219dd43 merge subdomain query into main query 2021-01-18 15:27:42 -08:00
Girish Ramakrishnan 55eb999821 Add to changes 2021-01-17 18:18:27 -08:00
Girish Ramakrishnan aedc8e8087 do not send flurry of down notification on box restart 2021-01-16 11:27:19 -08:00
Girish Ramakrishnan de7d27cd08 more module updates 2021-01-16 10:05:24 -08:00
Girish Ramakrishnan e4c7985e10 update many modules 2021-01-16 10:03:57 -08:00
Johannes Zellner fbcfa647ef Add basic owner transfer test 2021-01-15 21:13:13 +01:00
Girish Ramakrishnan 953c65788c mail: haraka update 2021-01-15 11:22:27 -08:00
Johannes Zellner b6473bc8f0 Add route to transfer ownership 2021-01-15 14:28:41 +01:00
Johannes Zellner a5cdd6087a Revert "To allow transfer ownership, a user has to be able to update its role if permissions are granted by current role"
This reverts commit c2f8da5507.
2021-01-15 14:16:55 +01:00
Johannes Zellner 24ffe5ec26 change volume test paths to not easily conflict 2021-01-14 21:15:54 +01:00
Johannes Zellner c2f8da5507 To allow transfer ownership, a user has to be able to update its role if permissions are granted by current role 2021-01-14 21:15:54 +01:00
Girish Ramakrishnan dbf3d3abd7 mail: better event log for bounces 2021-01-13 23:12:14 -08:00
Girish Ramakrishnan 9ee4692215 updatechecker: clear box update after update is done 2021-01-13 17:10:07 -08:00
Johannes Zellner 126f5e761b Ensure we have some default values for userRoles and adminMaxCount 2021-01-13 16:29:25 +01:00
Johannes Zellner 6874792670 Ensure features.userGroups has a default value 2021-01-13 14:48:58 +01:00
Johannes Zellner 6b3b4eb8b3 Use correct error variable 2021-01-13 12:33:40 +01:00
Girish Ramakrishnan d67598ab7e turn: use correct base image 2021-01-12 17:06:48 -08:00
Girish Ramakrishnan d8fd6be832 turn: fix for CVE-2020-26262 2021-01-12 17:03:30 -08:00
Girish Ramakrishnan a5dc65bda7 blacklist couchpotato on demo 2021-01-11 22:29:21 -08:00
Girish Ramakrishnan 6c8be9a47a add sickchill to demo blacklist 2021-01-11 22:04:12 -08:00
Girish Ramakrishnan 1a5fc894d6 Fix proxyAuth nginx config 2021-01-11 21:52:41 -08:00
Girish Ramakrishnan 7f324793b5 typo 2021-01-10 11:31:25 -08:00
Girish Ramakrishnan 0735353ab4 cloudron-setup: add --env unstable
this installs the latest unstable code but with prod appstore
2021-01-10 11:26:17 -08:00
Johannes Zellner 6ff2c5f757 Add apparmor as install dependency
Some hetzner images do not include that by default
2021-01-10 20:00:51 +01:00
Girish Ramakrishnan 29ab352846 proxyAuth: add exclusion path
had to move the ~ login/logout regexp inside. This is because of
https://www.ruby-forum.com/t/proxy-pass-location-inheritance/239135

What it says is that a regexp inside a matching location prefix is
given precedence regardless of how it appears in the file. This means
that the negative regexp got precedence over login|logout and thus
went into infinite redirect. By moving it to same level, the regexps
are considered in order.

Some notes on nginx location:

* First, it will match the prefixes (= and the /). If =, the matching stops.
  If /xx then the longest match is "remembered"
* It will then match the regex inside the longest match. First match wins
* It will then match the rest of the regex locations. First match win
* If no regex matched, it will then do the remembered longest prefix

fixes #762
2021-01-08 21:16:49 -08:00
Girish Ramakrishnan 4a6f36bc0e make the notfound page customizable
fixes #755
2021-01-08 11:02:09 -08:00
Girish Ramakrishnan 0ef0c77305 rename splash to notfound
part of #755
2021-01-08 10:13:01 -08:00
Girish Ramakrishnan 05c331172a Fix test 2021-01-07 22:21:41 -08:00
Girish Ramakrishnan 2414b44b6d Add to changes 2021-01-07 22:03:19 -08:00
Girish Ramakrishnan ca53449141 mailbox: list mailbox with alias info with a self join
fixes #738
2021-01-07 22:03:19 -08:00
Johannes Zellner 9342b2f0e3 Increase cloudron name to 64 2021-01-07 22:49:52 +01:00
Girish Ramakrishnan d15aa68bd7 eventlog: only merge ldap login events (and not dashboard)
fixes #758
2021-01-06 22:09:37 -08:00
Girish Ramakrishnan 624e34d02d eventlog: add logout
fixes #757
2021-01-06 21:57:56 -08:00
Girish Ramakrishnan af683b5fa4 add to changes 2021-01-06 21:47:48 -08:00
Girish Ramakrishnan f9c6c0102e mail: https://github.com/haraka/Haraka/pull/2893 2021-01-06 17:51:51 -08:00
Girish Ramakrishnan f71fbce249 mail: do not send client certs 2021-01-06 17:08:26 -08:00
Girish Ramakrishnan a184012205 apptask: set the memory limit based on the backup config
fixes #759
2021-01-06 15:26:51 -08:00
Girish Ramakrishnan 3bf50af09a mail: update haraka 2021-01-06 11:43:49 -08:00
Girish Ramakrishnan 29c513df78 apt: do not install recommended packages, only deps 2021-01-04 23:30:41 -08:00
Girish Ramakrishnan d2e03c009a redis: remove dead code 2021-01-04 19:36:43 -08:00
Girish Ramakrishnan a541c0e048 Fix installation on atlantic.net 2021-01-04 17:56:14 -08:00
Girish Ramakrishnan ead832ac73 volumes: collect du data
part of #756
2021-01-04 15:14:00 -08:00
Girish Ramakrishnan 370485eee6 avatar: use copy instead of rename
this is safer since rename() might fail with EXDEV on some servers
if /tmp and /home are on different filesystems.
2021-01-04 07:51:10 -08:00
Girish Ramakrishnan f3165c4e3b installer: move unzip to base image 2021-01-03 15:09:58 -08:00
Girish Ramakrishnan a8187216af installer: ipset is now in base image 2021-01-03 15:08:44 -08:00
Girish Ramakrishnan cf79e7f1ec Do not install xorg-server package
~# aptitude why xserver-xorg
i   collectd    Recommends libnotify4 (>= 0.7.0)
i A libnotify4  Recommends gnome-shell | notification-daemon
i A gnome-shell Recommends gdm3 (>= 3.10.0.1-3~)
i A gdm3        Recommends xserver-xorg
2021-01-03 14:53:47 -08:00
Girish Ramakrishnan 353369c1e9 mailer: make oom mail contain link to dashboard instead of docs 2021-01-02 12:26:34 -08:00
Girish Ramakrishnan 6507d95b98 rebuild mail container
https://github.com/haraka/Haraka/issues/2883
2021-01-02 12:12:08 -08:00
Girish Ramakrishnan 294413b798 Fix comment 2021-01-02 12:12:08 -08:00
Girish Ramakrishnan 51fd959e9d filemanager: better error message 2020-12-30 11:22:31 -08:00
Girish Ramakrishnan 8ddc72704e no need to bold version 2020-12-29 17:56:41 -08:00
Girish Ramakrishnan d1f9ae3df8 fix subject of the emails 2020-12-29 17:51:41 -08:00
Girish Ramakrishnan 28dee54a39 updates: only send email notifications when not auto-updating
fixes #749
2020-12-29 17:47:51 -08:00
Girish Ramakrishnan ff5702efc3 Better error message 2020-12-29 17:40:01 -08:00
Girish Ramakrishnan 663e0952fc move wellKnownJson to domains
after some more thought:
* If app moves to another location, user has to remember to move all this config
* It's not really associated with an app. It's to do with the domain info
* We can put some hints in the UI if app is missing.

part of #703
2020-12-23 17:13:22 -08:00
Girish Ramakrishnan 8a17e13ec4 automate wellknown setup
the main reason this is under app and not domain is because it let's
the user know that an app has to be installed for the whole thing to work.

part of #703
2020-12-23 15:20:53 -08:00
Girish Ramakrishnan a8436f8784 Fix external ldap test 2020-12-22 16:57:21 -08:00
Girish Ramakrishnan 93313abf33 test: emails are not sent anymore 2020-12-22 16:38:30 -08:00
Girish Ramakrishnan 246956fd0e groupMembers: add unique constraint
fixes #696
2020-12-22 16:18:15 -08:00
Girish Ramakrishnan b2fe43184c more changes 2020-12-22 10:13:17 -08:00
Girish Ramakrishnan 7bdeaca75b secure the provision and activation routes with a token
fixes #751
2020-12-21 23:33:31 -08:00
Girish Ramakrishnan e905c1edbe make function a bit more readable 2020-12-21 18:07:39 -08:00
Girish Ramakrishnan 88f24afae6 assume code 1 task 9 is oom
Fixes #750
2020-12-21 18:07:21 -08:00
Girish Ramakrishnan 33fb093aeb remove extra arg 2020-12-21 15:30:15 -08:00
Girish Ramakrishnan ac6c9e9b15 hasSubscription is always true
dashboard has logic for showing popup
2020-12-21 15:25:24 -08:00
Girish Ramakrishnan df5a333f30 add version to the updatechecker file 2020-12-21 12:41:23 -08:00
Girish Ramakrishnan 65290e52f7 persist update indicator across restarts
part of #749
2020-12-21 12:36:02 -08:00
Girish Ramakrishnan 9683bb6408 remove email notification for user add/remove
it's just very noisy. we anyway raise notifications
2020-12-21 08:45:18 -08:00
Girish Ramakrishnan e5209a1392 fix some typos 2020-12-20 14:41:16 -08:00
Girish Ramakrishnan 56707ac86a proxyauth: add 2fa
Fixes #748
2020-12-20 13:14:21 -08:00
Girish Ramakrishnan 64a4b712cc proxyAuth: add a hack to invalidate cache
when user goes to /logout and then goes to /, the browser will
serve up the cached / based on cache-control. This might make the
user believe they are not logged out.

fixes #753
2020-12-19 22:09:14 -08:00
Girish Ramakrishnan 3ccd527c8b acme2: fix logs 2020-12-19 16:24:56 -08:00
Girish Ramakrishnan 85d37233a2 proxyAuth: redirect to /login when logout
part of #753
2020-12-19 14:49:34 -08:00
Girish Ramakrishnan eff9d378e5 nfs: chown the backups for hardlinks to work 2020-12-18 17:14:42 -08:00
Girish Ramakrishnan 0f9a5c6b9a nfs: is prefix is empty, it errors 2020-12-18 14:41:59 -08:00
Girish Ramakrishnan a20bcbd570 mail: update haraka to 2.8.26 2020-12-17 17:57:19 -08:00
Girish Ramakrishnan 583c544cae regenerate nginx config when proxyAuth changes 2020-12-17 10:25:23 -08:00
Girish Ramakrishnan f55300eba5 reduce DO spaces copy part size 2020-12-15 14:37:18 -08:00
Girish Ramakrishnan a68ddcbbc4 Fix progress message 2020-12-14 19:58:44 -08:00
Girish Ramakrishnan 0723b7d672 reduce copy concurrency to keep most providers happy 2020-12-14 17:26:44 -08:00
Girish Ramakrishnan f5ed17e3d8 add ack flag to the debug 2020-12-14 16:07:09 -08:00
Girish Ramakrishnan 5ecf457a35 proxy auth: be explicit it is a 302 (default) 2020-12-13 13:24:59 -08:00
Girish Ramakrishnan 79a7e5d4a1 Also blacklist transmission on the demo 2020-12-13 12:36:13 -08:00
Girish Ramakrishnan 7d157b9343 Various 6.0.2 changes 2020-12-09 22:03:18 -08:00
Girish Ramakrishnan 67ccb180c9 update: set/unset appStoreId from the update route 2020-12-09 16:51:49 -08:00
Girish Ramakrishnan 822964116f remove dead code
appStoreId is never set to be cleared
2020-12-09 16:47:58 -08:00
Girish Ramakrishnan 360c3112ef use docker.inspect 2020-12-08 11:42:00 -08:00
Girish Ramakrishnan f2fba18860 scheduler: fix crash when container already exists 2020-12-08 11:36:57 -08:00
Girish Ramakrishnan cae9921159 sftp: use docker.inspect instead 2020-12-07 22:27:33 -08:00
Girish Ramakrishnan f497d5d309 fix thp disable on kernels that have it disabled 2020-12-07 11:38:11 -08:00
Girish Ramakrishnan 51a165dc7a add changes 2020-12-07 00:04:14 -08:00
Girish Ramakrishnan 9d4082356b mail: on location change, ignore error if dns cannot be updated 2020-12-07 00:02:56 -08:00
Girish Ramakrishnan 3b8bc47ee5 Set and clear timeout for external requests
otherwise, the server crashes for a write after timeout
2020-12-06 23:31:57 -08:00
Girish Ramakrishnan 78752fde7a app: add export route
Currently, the export route only creates the snapshot (the other side
of in-place import). In the future, the export route can export to a
custom backup config (like import).
2020-12-06 19:57:26 -08:00
Girish Ramakrishnan c6fd922fcd Blacklist adguard on the demo 2020-12-04 23:01:47 -08:00
Girish Ramakrishnan e90a211820 use REPLACE to ensure the key is inserted 2020-12-04 18:49:03 -08:00
Girish Ramakrishnan 8529485837 sftp: require admin by default (breaking change) 2020-12-04 18:45:52 -08:00
Girish Ramakrishnan 6810d823f5 collectd(df): convert byte string to string
this makes the graphs work
2020-12-04 12:10:59 -08:00
Girish Ramakrishnan 3e62f1913a acme2: issuer name has changed
There is now Let's Encrypt R3 and Let's Encrypt R4 etc

https://scotthelme.co.uk/lets-encrypts-new-root-and-intermediate-certificates/
2020-12-04 11:48:45 -08:00
Girish Ramakrishnan d23662c464 acme2: better logs 2020-12-04 11:47:19 -08:00
Girish Ramakrishnan 922c1ea317 acme2: fix error messages 2020-12-04 11:42:18 -08:00
Girish Ramakrishnan 258d81d7e9 mongo: bring mem limit in-line with others 2020-12-04 11:04:21 -08:00
Girish Ramakrishnan 1363e02603 graphite: bump up memory limit 2020-12-04 10:59:06 -08:00
Girish Ramakrishnan ccc65127f1 volumes: fix upload limit 2020-12-04 10:35:51 -08:00
Girish Ramakrishnan 3b38bb5d33 sftp: requireAdmin is true by default
for existing installs, it is off for backward compatibility
2020-12-04 00:25:37 -08:00
Girish Ramakrishnan 59c51c5747 volume: hostPath must exist on server 2020-12-03 23:13:20 -08:00
Girish Ramakrishnan ca17afc734 volumes: better hostPath validation 2020-12-03 23:05:06 -08:00
Girish Ramakrishnan 0b537fe163 error text: port is in use and not reserved 2020-12-03 22:27:59 -08:00
Girish Ramakrishnan 2a32bf3fc7 Add to changes 2020-12-03 21:58:27 -08:00
Girish Ramakrishnan 57c4d47657 Remove obsolete code 2020-12-03 17:36:32 -08:00
Girish Ramakrishnan 0371fe19ab Add back cn existence check 2020-12-03 13:35:50 -08:00
Girish Ramakrishnan 3de8fd5d92 fix issue where apps can sendmail with any username
a valid password is still required for this to work
2020-12-03 13:06:08 -08:00
Girish Ramakrishnan ce86cb892d the ip is now available in the appdb 2020-12-03 11:48:25 -08:00
Girish Ramakrishnan 9789ae3374 Remove redundant check 2020-12-03 11:46:57 -08:00
Girish Ramakrishnan e508893dcc mail: use env var to check if solr is enabled 2020-12-02 21:15:42 -08:00
Girish Ramakrishnan 699f04c9ff mail: disable solr if not enough memory 2020-12-02 17:56:49 -08:00
Girish Ramakrishnan 89c82fb001 send the raw healthcheck as part of status call 2020-12-02 17:07:33 -08:00
Girish Ramakrishnan b7fed04c12 roll back ldapjs
There is a crash upstream - https://github.com/ldapjs/node-ldapjs/pull/686
2020-12-02 14:50:03 -08:00
Girish Ramakrishnan 0ec5714271 Add to changes 2020-12-02 09:46:13 -08:00
Girish Ramakrishnan 5e483e4f3a delete any solr index when removing mailbox 2020-12-02 00:26:38 -08:00
Girish Ramakrishnan 84374b955e mail fts: enable prefix search 2020-12-01 23:45:55 -08:00
Girish Ramakrishnan 3a25c8da9f remove old code 2020-12-01 22:49:59 -08:00
Girish Ramakrishnan 5a5983cf96 mail: add solr to mail status 2020-12-01 22:45:33 -08:00
Girish Ramakrishnan 71c44a4c44 mail: only enable shared mailboxes when / is separator 2020-12-01 13:02:24 -08:00
Girish Ramakrishnan 41053d6857 validate backup folder and prefix 2020-12-01 12:46:02 -08:00
Girish Ramakrishnan 4287642308 firewall: add udp ports to allowed list 2020-11-30 10:26:39 -08:00
Girish Ramakrishnan 3934e59bd3 filemanager: allow downloading dirs as zip 2020-11-29 16:28:10 -08:00
Girish Ramakrishnan 9080e5c3ab tests: do not require passphrase 2020-11-29 11:19:28 -08:00
Girish Ramakrishnan 3d5599cdd9 b2: reduce copy part size
'Error copying snapshot/app_8b22dc8f-3e15-4314-8108-bcf1908a24df.tar.gz.enc (482405284 bytes): InternalError InternalError: too busy to complete copy - please try again' }
2020-11-29 11:16:05 -08:00
Girish Ramakrishnan 138d01e755 mail: acl update for getting shared mailboxes to show correctly 2020-11-28 16:30:12 -08:00
Girish Ramakrishnan 213ce114e3 disable thp
https://docs.mongodb.com/manual/tutorial/transparent-huge-pages/

redis complains loudly and this oftens results in support requests
2020-11-28 16:30:04 -08:00
Girish Ramakrishnan ad8b9cfc9f mail: enable acl 2020-11-27 18:14:49 -08:00
Girish Ramakrishnan de400dd652 mail: update mail container to remove explicit utf-8 on disk encoding 2020-11-26 22:42:11 -08:00
Girish Ramakrishnan 6218ee30a7 proxyAuth: inject CLOUDRON_PROXY_AUTH 2020-11-26 15:04:25 -08:00
Girish Ramakrishnan 976f072ef4 sftp: ubuntu 20 requires keys in legacy format 2020-11-26 11:53:28 -08:00
Girish Ramakrishnan f4762be58b add TODO 2020-11-25 22:25:36 -08:00
Girish Ramakrishnan 1b92ce08aa scheduler: suspend/resume jobs when apptask is active
the cron job container was holding on to the volume any container changes.
2020-11-25 22:16:38 -08:00
Girish Ramakrishnan 1d3d8288a9 unbound does not depend on box 2020-11-25 18:31:30 -08:00
Girish Ramakrishnan eec54e93bf Need nginx 1.18.0-2 for fresh ubuntu 16 installs
it fails with missing /run/nginx.pid message
2020-11-25 17:57:58 -08:00
Girish Ramakrishnan 77b965cada Add DNS to app containers as well
infra has to be bumped since we removed httpPort and moved to containerIp
2020-11-25 12:04:59 -08:00
Girish Ramakrishnan bcc9eda66c Remove ununsed constant 2020-11-25 10:33:40 -08:00
Girish Ramakrishnan 3a0b9d7b3b turn: add note 2020-11-25 10:19:01 -08:00
Girish Ramakrishnan e511b70d8f bring back resolvconf and unbound DNS
bd9c664b1a tried to remove it and use
the system resolver. However, we found that debian has a quirk that it adds
it adds the fqdn as 127.0.1.1. This means that the docker containers
resolve the my.example.com domain to that and can't connect.

This affects any apps doing a turn test (CLOUDRON_TURN/STUN_SERVER)
and also apps like SOGo which use the mail server hostname directly (since
they require proper certs).

https://www.debian.org/doc/manuals/debian-reference/ch05.en.html#_the_hostname_resolution

So, the solution is to go back to unbound, now that port 53 binding is specially
handled anyway in docker.js
2020-11-25 10:02:43 -08:00
Girish Ramakrishnan 25cc60e648 mail: change the namespace separator to / 2020-11-24 12:55:58 -08:00
Johannes Zellner d1e05dcb6f Make proxyauth login translatable 2020-11-24 20:57:13 +01:00
Girish Ramakrishnan 8cfd859711 mail: make eventlog search also searches type field
Fixes #740
2020-11-23 16:22:16 -08:00
Girish Ramakrishnan 7b3b826f87 DNS fixes that work on all ubuntu versions 2020-11-23 00:27:17 -08:00
Girish Ramakrishnan 195c9bd81f check the type of userIds array 2020-11-22 21:42:08 -08:00
Girish Ramakrishnan a8928d26d1 Fix appdb get query
the get() query was wrong when we had multiple port bindings.

we did apps JOIN X JOIN Y JOIN Z. This will return apps times x times y times z rows.
this just accidentally worked in the past. when we have multiple mounts,
we get duplicate values now.

the fix is do the joins separately and then merge them together.

an alternate approach to this mega query is to SET TRANSACTION SERIALIZABLE and do
multiple selects. but that requires database.js support which is a bit of work (and not
sure how it works with "connections").
2020-11-22 16:03:41 -08:00
Johannes Zellner ef287d4436 fix language test
Ideally we would use a copy of the dashboard, either way we kinda have
to rely on repo layouts to find it
2020-11-22 10:34:35 +01:00
Girish Ramakrishnan 6ae1de6989 test: make apps test work 2020-11-21 23:25:28 -08:00
Girish Ramakrishnan 9c810ce837 hack to make translation tests pass 2020-11-21 23:07:30 -08:00
Girish Ramakrishnan ba913bb949 another console.error to debug 2020-11-21 18:32:38 -08:00
Girish Ramakrishnan 58487b729a use debug since it floods the test logs 2020-11-21 18:28:45 -08:00
Girish Ramakrishnan bf73cbaf97 test: make the certs test pass again
generate certs for next 10 years
2020-11-21 18:17:37 -08:00
Girish Ramakrishnan 1db868bf9c httpPaths: add trailing slash to proxy_pass
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass

"If the proxy_pass directive is specified with a URI, then when a request
is passed to the server, the part of a normalized request URI matching the
location is replaced by a URI specified in the directive"
2020-11-21 14:37:39 -08:00
Girish Ramakrishnan d331597bff proxyAuth: allow protecting specific subpath
while I don't think this is useful for apps, it is useful for e2e test atleast
2020-11-20 18:29:55 -08:00
Girish Ramakrishnan 71648d92ae proxyAuth: authorization logic 2020-11-20 17:54:17 -08:00
Girish Ramakrishnan 735485b539 rename variable 2020-11-20 17:52:22 -08:00
Girish Ramakrishnan 09c8248e31 move back docker network creation to start.sh
dockerproxy and unbound rely on it.
2020-11-20 17:22:57 -08:00
Girish Ramakrishnan c0b0029935 statically allocate app container IPs
We removed httpPort with the assumption that docker allocated IPs
and kept them as long as the container is around. This turned out
to be not true because the IP changes on even container restart.

So we now allocate IPs statically. The iprange makes sure we don't
overlap with addons and other CI app or JupyterHub apps.

https://github.com/moby/moby/issues/6743
https://github.com/moby/moby/pull/19001
2020-11-20 16:19:59 -08:00
Johannes Zellner 64af278f39 add missing curly brackets 2020-11-21 00:13:07 +01:00
Johannes Zellner 57dabbfc69 Translate welcome and password reset email subjects 2020-11-20 22:38:52 +01:00
Girish Ramakrishnan 279f7a80c5 make appstatus work again for apps 2020-11-20 12:04:58 -08:00
Girish Ramakrishnan b66fdb10f2 apptask: Use debugApp 2020-11-20 11:21:06 -08:00
Johannes Zellner 84c1703c1a Translate password reset email 2020-11-20 16:18:24 +01:00
Johannes Zellner f324d50cef Translate welcome mail 2020-11-20 16:10:13 +01:00
Johannes Zellner 93a1e6fca8 Avoid translation file caching 2020-11-20 16:10:13 +01:00
Girish Ramakrishnan 4d55783ed8 unbound: start it after docker 2020-11-19 23:22:11 -08:00
Girish Ramakrishnan aad50fb5b2 add routes to get/set solr config 2020-11-19 20:19:24 -08:00
Johannes Zellner fb4ba5855b Make emails translatable 2020-11-20 00:43:12 +01:00
Johannes Zellner fbe5f42536 Split welcome email between html and text version 2020-11-19 22:29:29 +01:00
Girish Ramakrishnan 7663360ce6 add to changes 2020-11-19 11:20:22 -08:00
Girish Ramakrishnan 0a3aad0205 Add httpPaths support 2020-11-19 11:02:53 -08:00
Girish Ramakrishnan cde42e5f92 postgresql: rebuild (for new manifest) 2020-11-19 09:35:55 -08:00
Girish Ramakrishnan fd965072c5 Remove custom dns when creating app container 2020-11-19 01:10:50 -08:00
Girish Ramakrishnan d703d1cd13 remove httpPort
we can just use container IP instead of all this httpPort exporting magic.
this is also required for exposing httpPaths feature (we have to otherwise
have multiple httpPorts).
2020-11-19 00:38:52 -08:00
Girish Ramakrishnan bd9c664b1a Free up port 53
It's all very complicated.

Approach 1: Simple move unbound to not listen on 0.0.0.0 and only the internal
ones. However, docker has no way to bind only to the "public" interface.

Approach 2: Move the internal unbound to some other port. This required a PR
for haraka - https://github.com/haraka/Haraka/pull/2863 . This works and we use
systemd-resolved by default. However, it turns out systemd-resolved with hog the
lo and thus docker cannot bind again to port 53.

Approach 3: Get rid of systemd-resolved and try to put the dns server list in
/etc/resolv.conf. This is surprisingly hard because the DNS listing can come from
DHCP or netplan or wherever. We can hardcode some public DNS servers but this seems
not a good idea for privacy.

Approach 4: So maybe we don't move the unbound away to different port after all.
However, all the work for approach 2 is done and it's quite nice that the default
resolver is used with the default dns server of the network (probably a caching
server + also maybe has some home network firewalled dns).

So, the final solution is to bind to the make docker bind to the IP explicity.
It's unclear what will happen if the IP changes, maybe it needs a restart.
2020-11-18 23:25:56 -08:00
Johannes Zellner ae94ff1432 Send Cloudron default language via status call 2020-11-18 23:45:16 +01:00
Johannes Zellner b64acb412e Add cloudron-translation-update script 2020-11-18 23:16:42 +01:00
Johannes Zellner cbc5ec7d89 List languages from dashboard dist/translation 2020-11-18 18:39:55 +01:00
Johannes Zellner 5401dc9e18 Update ldapjs module 2020-11-18 09:33:06 +01:00
Girish Ramakrishnan 9b37597ac8 Bump up max_allowed_packet
https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_allowed_packet
has it already set to 64M by default. I also saw some mysql connection
drop issues and think this is the reason
2020-11-17 19:45:28 -08:00
Johannes Zellner 784c8b2bd2 Add route to get available languages 2020-11-18 00:10:06 +01:00
Johannes Zellner 2388fe5047 Add cloudron global language setting 2020-11-17 22:44:03 +01:00
Girish Ramakrishnan 064eff0ac1 add changes 2020-11-16 22:50:56 -08:00
Girish Ramakrishnan b5c933494a linode: cannot destructure null/undefined
if we have an error, the destructure will fail
2020-11-16 22:37:53 -08:00
Girish Ramakrishnan 8c0bd97064 mail: owner can be a group 2020-11-13 00:31:34 -08:00
Girish Ramakrishnan 2ca9534715 add some comments on the ldap routes 2020-11-12 22:13:24 -08:00
Girish Ramakrishnan 641704a741 proxyauth: support basic auth
this is required for apps like transmission
2020-11-11 15:11:36 -08:00
Girish Ramakrishnan 82d88d375e sftp: fix crash 2020-11-11 11:13:30 -08:00
Johannes Zellner 751caa7b3b Prefix base64 image data 2020-11-11 11:24:20 +01:00
Girish Ramakrishnan 7e16128b11 proxyauth: render as ejs tos end app title and icon 2020-11-11 00:36:02 -08:00
Girish Ramakrishnan 008fa09877 proxyauth: redirect correctly after login 2020-11-11 00:01:36 -08:00
Girish Ramakrishnan 045963afe5 serve proxyauth login file from the dashboard 2020-11-10 21:18:50 -08:00
Girish Ramakrishnan b799df3626 authproxy -> proxyauth 2020-11-10 20:04:31 -08:00
Girish Ramakrishnan 772df6f9af typo 2020-11-10 19:49:57 -08:00
Girish Ramakrishnan 72cb383f2c proxy auth: create token secret 2020-11-10 17:20:27 -08:00
Girish Ramakrishnan 625dc7c49b Add proxyAuth as an addon 2020-11-10 16:50:36 -08:00
Girish Ramakrishnan 86916a94de allow 401 and 403 errors to pass health check
way too many WP sites use some plugin to block health check routes.
maybe some day we will have dynamic health check route settable by user.
2020-11-10 16:50:36 -08:00
Girish Ramakrishnan 71666a028b add support for protected sites
https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-subrequest-authentication/
https://gock.net/blog/2020/nginx-subrequest-authentication-server/
https://github.com/andygock/auth-server
2020-11-10 01:06:39 -08:00
Girish Ramakrishnan 01e6301332 collectd: adjust collectd config when app is stopped and started 2020-11-09 10:37:22 -08:00
Johannes Zellner 13050f7bff Give log files better names on download 2020-11-09 11:07:16 +01:00
Girish Ramakrishnan bedcd6fccf Disable the timeout altogether for chunk to upload 2020-11-06 14:47:14 -08:00
Girish Ramakrishnan df8a71cd8b Each chunk can take up to 30 mins to upload 2020-11-06 00:05:53 -08:00
Girish Ramakrishnan a113ece22b Still have to preserveEnv for the env vars to make it across sudo 2020-11-05 16:13:42 -08:00
Girish Ramakrishnan a63c2cfdf2 reverse this since it makes better reading 2020-11-05 16:08:57 -08:00
Girish Ramakrishnan 8f78a9dcde No need to pass --expose-gc
http://sambal.org/2014/02/passing-options-node-shebang-line/ was a cool trick but not needed at all.

https://bitbucket.org/chromiumembedded/cef/issues/483/dont-always-add-the-expose-gc-v8-flag
says it will change behavior in ways we don't want.
2020-11-05 16:07:28 -08:00
Girish Ramakrishnan 02eb362f37 Set the heap size with large backup memory limits
I had to also give the server some more swap for the backup to succeed
2020-11-05 16:06:12 -08:00
Girish Ramakrishnan f79263a92a backups: periodically dump heap space info 2020-11-05 16:06:09 -08:00
Girish Ramakrishnan cd95da6d35 Typo in message 2020-11-05 09:59:13 -08:00
Johannes Zellner 5ab2c9afaa Use new sftp image to fix chown 2020-11-04 15:11:41 +01:00
Johannes Zellner e77201099d Encode filemanager route paths correctly and do not expect starts with / 2020-11-04 13:58:53 +01:00
Johannes Zellner 30a4c00f35 Update sftp addon to avoid crash when overwrite property is missing 2020-11-03 21:27:24 +01:00
Girish Ramakrishnan e68db4ce57 Aim for 60% used space 2020-11-02 23:42:53 -08:00
Girish Ramakrishnan b5a83ab902 demo: blacklist alltube as well 2020-11-02 15:16:21 -08:00
Girish Ramakrishnan 2c9efea733 Use debug instead of console.error 2020-10-30 11:07:51 -07:00
Girish Ramakrishnan 9615dc1458 Mount volumes into the file browser 2020-10-30 11:05:47 -07:00
Girish Ramakrishnan f50a8482c3 Fix error code handling 2020-10-30 10:04:00 -07:00
Girish Ramakrishnan cd3dc00f2f Do not allow duplicate mounts 2020-10-29 23:07:48 -07:00
Girish Ramakrishnan 65eae30a48 Mount API fixes 2020-10-29 22:04:38 -07:00
Girish Ramakrishnan fa4392df09 Fix docker.getBinds() 2020-10-29 11:47:37 -07:00
Johannes Zellner f8d6fd80d5 Do not crash if app.volumes does not exist 2020-10-29 12:09:15 +01:00
Girish Ramakrishnan 88ed545830 rename appVolumes to appMounts 2020-10-28 22:06:33 -07:00
Girish Ramakrishnan 4388f6e87c Send volumes in REST response 2020-10-28 19:33:32 -07:00
Girish Ramakrishnan 6157364e20 Cannot update a volume (otherwise, we have to re-configure apps) 2020-10-28 17:04:24 -07:00
Girish Ramakrishnan 96999e399d volume: use the load pattern
this way we can stash info in the eventlog
2020-10-28 15:56:54 -07:00
Girish Ramakrishnan 6a3df679fa Add volume management
the volumes table can later have backup flag, mount options etc
2020-10-28 15:31:21 -07:00
Johannes Zellner 03e49c59e2 Revert "more changes"
This reverts commit d69af56c90.
2020-10-28 16:16:10 +01:00
Girish Ramakrishnan b525b6e4fa fix code style 2020-10-27 17:15:19 -07:00
Girish Ramakrishnan 5541b89cf7 Revert "redis: add optional flag"
This reverts commit 0cac5610c8.
2020-10-27 08:48:45 -07:00
Girish Ramakrishnan aaeed5d18b Revert "Another check for redis services configs"
This reverts commit d6c3c8a294.
2020-10-27 08:48:17 -07:00
Johannes Zellner d6c3c8a294 Another check for redis services configs 2020-10-27 14:47:52 +01:00
Johannes Zellner d337fc6d47 Do not crash if an app does not have a redis service config 2020-10-27 09:32:22 +01:00
Johannes Zellner 2d897d8537 A task crash should be visible in the task log 2020-10-27 09:20:26 +01:00
Girish Ramakrishnan 12b101e04f Make the timeout 30 seconds everywhere 2020-10-26 14:08:34 -07:00
Girish Ramakrishnan d69af56c90 more changes 2020-10-26 10:04:37 -07:00
Girish Ramakrishnan 0cac5610c8 redis: add optional flag 2020-10-24 10:34:30 -07:00
Girish Ramakrishnan d0afcf6628 Disable updating the cloudron user in demo mode 2020-10-23 11:41:39 -07:00
Girish Ramakrishnan 37fa27d54f more changes 2020-10-22 10:04:27 -07:00
Girish Ramakrishnan be4fed2c19 postgresql: whitelist pgcrypto extension for loomio 2020-10-22 08:56:55 -07:00
Johannes Zellner 47d02d8c4f Update sftp addon container 2020-10-22 15:52:27 +02:00
Girish Ramakrishnan 4881d8e3a1 Add option to allow non-admins to access SFTP 2020-10-21 23:38:13 -07:00
Johannes Zellner cc618abf58 Update sftp image 2020-10-20 12:44:38 +02:00
Girish Ramakrishnan 546e381325 skip downloading image if image present locally
if we use build service app locally (without push), then we can skip
the download altogether.
2020-10-19 22:22:29 -07:00
Girish Ramakrishnan 9d1bb29a00 sftp: Make extract work 2020-10-19 19:58:39 -07:00
Girish Ramakrishnan 876d0d5873 sftp: init and access API with a token 2020-10-19 19:13:54 -07:00
Girish Ramakrishnan 2aa5c387c7 branding: add template variables
we can now have %YEAR% and %VERSION% in the footer
2020-10-18 10:19:13 -07:00
Girish Ramakrishnan 9ca8e49a4e More changes 2020-10-15 16:46:22 -07:00
Girish Ramakrishnan 6ceed03f6b 5.6.3 changes 2020-10-12 21:09:47 -07:00
Girish Ramakrishnan 4836b16030 postgresql: make the locale configurable 2020-10-12 18:57:34 -07:00
Girish Ramakrishnan f9f44b18ad suppress reset-failed warning message 2020-10-12 10:08:07 -07:00
Girish Ramakrishnan d4f5b7ca34 cloudron-setup: mention "After reboot" 2020-10-08 23:23:05 -07:00
Girish Ramakrishnan 9b57329f56 Ghost password can now only be used once 2020-10-08 22:19:18 -07:00
Girish Ramakrishnan 0064ac5ead reduce the duration of self-signed certs
https://support.apple.com/en-us/HT210176
https://forum.cloudron.io/topic/3346/automatically-generated-self-signed-wildcard-certificate-doesn-t-appear-to-be-able-to-be-trusted-by-ios-13-or-greater
2020-10-08 14:39:23 -07:00
Girish Ramakrishnan f2489c0845 some logs for tracking the cron issue 2020-10-07 14:47:51 -07:00
Girish Ramakrishnan dca345b135 restore: disable IP based api calls after all activation tasks
the restore code relies on the status call to get the domain to
redirect. if the IP/v1/cloudron/status does not respond, it will
fail the redirection.
2020-10-07 10:57:19 -07:00
Johannes Zellner 645c1b9151 Limit log files to last 1000 lines 2020-10-07 17:42:35 +02:00
Johannes Zellner 678fca6704 For app tickets, send the log files along 2020-10-06 17:53:07 +02:00
Johannes Zellner b74fae3762 Support SSH remote enabling on ticket submission 2020-10-06 16:01:59 +02:00
Johannes Zellner 2817ea833a Add enableSshSupport option to support tickets 2020-10-06 16:01:59 +02:00
Girish Ramakrishnan b7ed6d8463 add changes 2020-10-05 21:32:25 -07:00
Girish Ramakrishnan 005c33dbb5 locations (primary, secondary) of an app must be updated together
do the delete first to clear out all the domains. this way, you can
move primary to redirect in a single shot.
2020-10-05 16:16:58 -07:00
Girish Ramakrishnan 4176317250 Fix version in changes to prepare for 5.6.2 2020-10-05 12:45:12 -07:00
Girish Ramakrishnan bbd562f711 Add changes 2020-10-04 16:40:47 -07:00
Girish Ramakrishnan a19505a708 Fix postgresql template 2020-10-01 15:47:59 -07:00
Girish Ramakrishnan 1eed16bc97 postgresql: set collation order explicitly 2020-10-01 12:04:52 -07:00
Girish Ramakrishnan d9f88985fe rsync: create destination file only when source is available
if the source disappears, the upload() in the backend creates the file
as 'root'. the chown is never done because the read stream errored.
As a result of permissions, cp fails to hardlink because the hardlink
is run as yellowtent user.

fixes #741
2020-09-30 20:12:17 -07:00
Girish Ramakrishnan a57e33e8d1 Update readme with hotfix instructions 2020-09-30 09:55:17 -07:00
Girish Ramakrishnan b4552ddb5f more changes 2020-09-29 14:46:52 -07:00
Girish Ramakrishnan 1da2450b10 gcs: use copy concurrency 2020-09-28 22:03:08 -07:00
Girish Ramakrishnan 9536b42244 Add changes 2020-09-28 10:27:34 -07:00
Johannes Zellner dd75cdb37e Don't explicitly sync the filesystems on reboot
This will happen during unmount anyways but will first terminate all
processes
2020-09-25 19:11:15 +02:00
Johannes Zellner 3b3e537797 Update ldapjs dependency to 2.2.0 2020-09-24 12:50:14 +02:00
Girish Ramakrishnan 0f9168052a nginx: add separate endpoint for ip/setup screens
'setup' endpoint for setup/restore. we show the setup wizard.
'ip' endpoint is post activation. we show a splash screen here.

Also, the https://ip will not respond to any api calls anymore
(since this will leak the admin fqdn otherwise).

We should probably make this customizable at some point.

Fixes #739
2020-09-23 23:07:40 -07:00
Girish Ramakrishnan eb47476c83 collectd: remove nginx status collection
we don't use this at all
2020-09-23 16:09:46 -07:00
Girish Ramakrishnan 7b04817874 rename writeAdmin to writeDashboard 2020-09-23 15:45:04 -07:00
Girish Ramakrishnan c7a7456ec9 more test fixing 2020-09-23 15:31:07 -07:00
Girish Ramakrishnan e422dd1198 turn service must be rebuilt on dashboard domain change
restart only restarts the container and does not affect the env
variables.
2020-09-23 15:18:28 -07:00
Girish Ramakrishnan a75928d805 Fix coding style 2020-09-23 15:13:23 -07:00
Girish Ramakrishnan fb2c5a85b6 Fix cloudron_ghost.json tests 2020-09-23 14:40:45 -07:00
Girish Ramakrishnan 4de2e381ff npm update 2020-09-23 14:08:27 -07:00
Girish Ramakrishnan 4da8c8d6db updateServiceConfig: remove retry from platform code 2020-09-22 21:46:11 -07:00
Girish Ramakrishnan 3c565defca retry setting memory of services 2020-09-22 21:42:47 -07:00
Girish Ramakrishnan 191be658d5 firewall: fix race where blocklist was added after docker rules 2020-09-22 12:02:40 -07:00
Girish Ramakrishnan 1f209d0fb4 fix some comments 2020-09-22 11:43:14 -07:00
Girish Ramakrishnan ba91e1dfb2 Add change 2020-09-21 22:10:58 -07:00
Girish Ramakrishnan 6766884cd8 Update changes 2020-09-21 16:50:13 -07:00
Girish Ramakrishnan b075140e76 /dev/dri may not exist
In ubuntu 16, it doesn't exist.
See also https://forum.cloudron.io/topic/3189/error-server-error-http-code-500-server-error
2020-09-21 15:59:17 -07:00
Girish Ramakrishnan aa8586d273 bump mysql for connection limit 2020-09-17 19:24:24 -07:00
Girish Ramakrishnan 9b2a3d23b2 cloudron-setup: there could be owners who have not selected a username yet 2020-09-17 13:56:04 -07:00
Girish Ramakrishnan 6a43a4bd20 unlink ghost file automatically on successful login 2020-09-17 10:46:32 -07:00
Girish Ramakrishnan 8c78889e88 namecheap: fix crash if server returns invalid response 2020-09-16 16:44:40 -07:00
Girish Ramakrishnan 873159b793 Add to changes 2020-09-16 16:05:09 -07:00
Girish Ramakrishnan b5823d3210 use legacy password scheme in mysql 8
https://github.com/db-migrate/node-db-migrate/issues/610

part of #684
2020-09-16 00:03:13 -07:00
Girish Ramakrishnan cd99c22f64 Fix collectd in focal
part of #684
2020-09-16 00:02:58 -07:00
Girish Ramakrishnan baa5122fcb Update mysql and docker
part of #684
2020-09-15 21:58:40 -07:00
Girish Ramakrishnan 5447aa7c80 missed this one 2020-09-15 14:46:47 -07:00
Girish Ramakrishnan 933918ea27 Fix docs url 2020-09-15 14:46:22 -07:00
Girish Ramakrishnan cbbcdc5df1 regenerate nginx configs
Users are seeing SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET. Probably related
some of the app configs had ssl_session_tickets off and some didn't . It seems nginx
has some issue if they are inconsistent (see also https://github.com/nginx-proxy/nginx-proxy/issues/580#issuecomment-249587149).

https://forum.cloudron.io/topic/3157/ssl-error-after-upgrading-to-5-6-0-on-ubuntu-16
2020-09-15 08:26:41 -07:00
Girish Ramakrishnan 4dfa7b132d ignore duplicates 2020-09-14 13:53:58 -07:00
Girish Ramakrishnan fb5bfaa2bd read does not read last line without a newline 2020-09-14 13:52:10 -07:00
Girish Ramakrishnan 20e206fa43 migrate blocklist to a txt file
this allows easy copy/pasting of existing deny lists which contain
comments and blank lines
2020-09-14 12:10:29 -07:00
Girish Ramakrishnan 467fa59023 Fix timeout issue when adding lots of network ranges 2020-09-14 09:56:35 -07:00
Girish Ramakrishnan 166c06c628 log the partSize 2020-09-10 00:09:54 -07:00
Girish Ramakrishnan 5ff3c8961c mail: log denial of max mail size 2020-09-09 22:48:43 -07:00
Girish Ramakrishnan 08f33f0e78 Add mail location audit log 2020-09-09 22:31:50 -07:00
Girish Ramakrishnan 0c5a637203 Fix progress indicator when mail location is being changed 2020-09-09 21:49:44 -07:00
Girish Ramakrishnan e3b4fdb6b1 better logs of the scheduler 2020-09-09 20:09:16 -07:00
Girish Ramakrishnan e730a6e282 log: do not show app update message for no updates 2020-09-09 19:26:45 -07:00
Girish Ramakrishnan 722808a0e4 firewall: make sure blocklist is first in the forward chain 2020-09-09 17:47:20 -07:00
Girish Ramakrishnan eae33161c1 Forgot the CLOUDRON_ prefix 2020-09-08 19:33:59 -07:00
Girish Ramakrishnan f14df141f7 Add MAIL_SERVER_HOST
This points to the mail fqdn
2020-09-08 19:33:23 -07:00
Girish Ramakrishnan f7a4330cd1 Add CLOUDRON_LDAP_HOST
We have MYSQL_HOST, POSTGRESQL_HOST etc. Just this LDAP has _SERVER
2020-09-08 19:32:21 -07:00
Johannes Zellner 23474c9752 Only disable motd-news if file exists 2020-09-04 10:49:07 +02:00
Girish Ramakrishnan fc08f9823e s3: copy parts in parallel 2020-09-03 14:31:56 -07:00
Girish Ramakrishnan 639bddb4b7 Do not use app.manifest.title since it may not be set for custom apps 2020-09-03 13:49:38 -07:00
Girish Ramakrishnan f87b32fc7b do not allow setting blocklist in demo mode 2020-09-02 23:04:48 -07:00
Girish Ramakrishnan 468ad6d578 Add some new backup regions 2020-09-02 19:39:58 -07:00
Girish Ramakrishnan 8b5c7d3d87 make http redirect to https://final-destination 2020-09-02 18:56:22 -07:00
Girish Ramakrishnan e791084793 bump timeout to 24 hours 2020-09-02 18:19:25 -07:00
Girish Ramakrishnan 316a1ae2c5 only scale back containers on infra change 2020-09-02 18:13:08 -07:00
Girish Ramakrishnan 71beca68dc Fix nginx reload race 2020-09-02 18:02:22 -07:00
Johannes Zellner aae79db27a Mention that we use task types also in the dashboard 2020-09-02 17:06:25 +02:00
Girish Ramakrishnan 6f188da2a6 Do not call onActivated when not activated
regression caused by ba29889f54
2020-09-01 15:35:43 -07:00
Girish Ramakrishnan 9ae4ce82a7 scheduler: stash the containerId in the state
the container id will change when the app is re-configured.
in the future, maybe it's better to do this like sftp.rebuild()
2020-09-01 12:56:06 -07:00
Girish Ramakrishnan 5adfa722d4 Add some debug information 2020-09-01 12:35:31 -07:00
Girish Ramakrishnan c26dda7cc9 require owner for network blocklist 2020-08-31 22:53:22 -07:00
Girish Ramakrishnan b7440ee516 Do IP based check first before accepting port based checks 2020-08-31 21:55:45 -07:00
Girish Ramakrishnan e4b06b16a9 firewall: implement blocklist 2020-08-31 21:46:07 -07:00
Girish Ramakrishnan 491af5bd9a stop apps before updating the databases because postgres will "lock" them preventing import 2020-08-31 17:53:29 -07:00
Girish Ramakrishnan 9b67ab9713 typo 2020-08-31 08:58:38 -07:00
Girish Ramakrishnan f0a62600af No need to accept them here since the ports are managed by docker 2020-08-31 08:58:02 -07:00
Girish Ramakrishnan dd5dfd98b7 ensure box update backups are also preserved for 3 weeks 2020-08-30 21:38:13 -07:00
Girish Ramakrishnan d5ec38c4db do not restrict postgresql db memory
see also 3ea6610923
2020-08-30 21:37:57 -07:00
Girish Ramakrishnan f945463dbe postgresql: enable uuid-ossp extension 2020-08-26 19:29:41 -07:00
Girish Ramakrishnan cf9439fb3b systemd 237 ignores --nice value in systemd-run 2020-08-26 17:30:47 -07:00
Girish Ramakrishnan 6901847c49 Update mail container for banner changes
fixes #341
2020-08-24 14:30:39 -07:00
Girish Ramakrishnan c54c25c35e fix task signature 2020-08-24 12:57:48 -07:00
Girish Ramakrishnan 5728bce6bc Fix typos 2020-08-24 10:28:53 -07:00
Girish Ramakrishnan d752403ed6 mail: add API to get/set banner
part of #341
2020-08-24 08:56:13 -07:00
Girish Ramakrishnan a48c08bd23 Fix async loop 2020-08-23 18:21:00 -07:00
Girish Ramakrishnan e46bbe8546 Add missing changes 2020-08-22 16:43:00 -07:00
Girish Ramakrishnan f5c8f18980 spamassassin: custom configs and wl/bl 2020-08-22 15:57:26 -07:00
Johannes Zellner 2d2270a337 Ensure stderr and exceptions also go to logfile
Bring back supererror for stacktraces when no Error object is throwing
2020-08-21 10:40:32 +02:00
Johannes Zellner d315c53ff8 Only rebuild sftp is something has changed 2020-08-21 09:24:06 +02:00
Girish Ramakrishnan d36b06acf7 Fix mail location route 2020-08-20 23:12:43 -07:00
Girish Ramakrishnan 2299af1dba Add route to set max email size 2020-08-20 22:18:27 -07:00
Girish Ramakrishnan e25ccc5e9a Double the timeout for upload now that chunks can have custom sizes 2020-08-20 16:50:58 -07:00
Girish Ramakrishnan 3ea6610923 do not restrict memory on startup of database addons
this helps the import case where we need all the memory we can get.
we scale the memory down once platform is ready in any case.
2020-08-20 11:16:35 -07:00
Girish Ramakrishnan 2d50f10fd6 Fix some typos 2020-08-19 23:14:05 -07:00
Girish Ramakrishnan 81d0637483 Allow box auto update pattern to be configurable
We just use the current app auto update pattern as the default.
There is now only one pattern for box and app updates.

Fixes #727
2020-08-19 22:09:41 -07:00
Girish Ramakrishnan 6c4df5abf0 unify update check into a single job 2020-08-19 21:43:12 -07:00
Girish Ramakrishnan 2eb0b5eedd remove unused parse-links module 2020-08-19 15:53:12 -07:00
Girish Ramakrishnan 0e00492f54 backups: make part size configurable 2020-08-19 14:39:20 -07:00
Girish Ramakrishnan b84a62eb5d Add to changes 2020-08-19 13:35:42 -07:00
Johannes Zellner c41ed95afe Remove wrong assert 2020-08-19 19:22:10 +02:00
Johannes Zellner fe07013383 Ensure only one sftp rebuild is in progress 2020-08-19 19:13:34 +02:00
Johannes Zellner 4f9cb9a8a1 sftp.rebuild does not need options anymore 2020-08-19 19:08:12 +02:00
Johannes Zellner ec5129d25b Rebuild sftp addon after an apptask 2020-08-19 18:23:44 +02:00
Johannes Zellner 6a781c62ec Improve task progress values
0: not yet handled
1: queued
2: started
100: finished
2020-08-19 16:58:53 +02:00
Girish Ramakrishnan c01ee83cd7 add note on why we delete 2020-08-18 23:53:14 -07:00
Girish Ramakrishnan cc591e399d scheduler: make the container run in same networking space to prevent further churn
idea comes from https://github.com/moby/moby/pull/9402#issuecomment-67259655
and https://github.com/moby/moby/pull/9402#issuecomment-67224239

see also:
https://github.com/moby/moby/issues/9098
https://github.com/moby/moby/pull/9167
https://github.com/moby/moby/issues/12899#issuecomment-97816048 (exec mem leak)
https://github.com/moby/moby/pull/38704

part of #732
2020-08-18 23:44:53 -07:00
Girish Ramakrishnan 7462c703f3 typo 2020-08-18 21:40:10 -07:00
Girish Ramakrishnan 879a6b4202 do not error if container already exists 2020-08-18 21:15:54 -07:00
Girish Ramakrishnan 0ae8dc1040 scheduler: reduce container churn
When we have a lot of app, docker has a tough time keeping up with
the container churn.

The reason why we don't use docker exec is that there is no way
to delete or manage exec containers.

Fixes #732
2020-08-18 20:26:19 -07:00
Girish Ramakrishnan 242548b36a If swap file exists, do nothing
this gives users more control on how to allocate swap
2020-08-18 12:57:51 -07:00
Girish Ramakrishnan 252aedda25 remove verbose logs 2020-08-18 12:46:55 -07:00
Girish Ramakrishnan 3507269321 Allow mail server name to be configurable
Fixes #721
2020-08-17 21:49:59 -07:00
Girish Ramakrishnan 9a5dce33db Be explicit about mailserver routes 2020-08-17 16:26:04 -07:00
Girish Ramakrishnan c4101a62ed rename function to setupDnsAndCert
this way, we can reuse this logic for the mail domain as well
2020-08-17 16:18:48 -07:00
Girish Ramakrishnan f52037f305 Remove cloudron.setupDashboard 2020-08-17 16:18:19 -07:00
Girish Ramakrishnan 03bd67c4e7 coding style 2020-08-17 16:18:12 -07:00
Girish Ramakrishnan 1eef239392 setting dashboard domain now only updates dashboard domain (and not mail)
part of #721
2020-08-17 16:09:20 -07:00
Girish Ramakrishnan d1e14ed691 rename function to setupDashboarDnsAndCert 2020-08-17 15:42:15 -07:00
Girish Ramakrishnan 60a787ce3d If db name exists, re-use it (for repair mode) 2020-08-17 12:04:02 -07:00
Girish Ramakrishnan f96bc6d5f4 keep mongodb database names short 2020-08-17 10:28:49 -07:00
Girish Ramakrishnan 5d439d9e79 Revert "Update mongodb to 4.2.8"
This reverts commit 9d2284add7.

We started updating because some users hit this error

MongoError: namespace name generated from index name "f6d689d0-0098-4ee5-b3ed-a812a75d9ae8.rocketchat_livechat_inquiry.$queueOrder_1_estimatedWaitingTimeQueue_1_estimatedServiceTimeAt_1" is too long (127 byte max)

MongoDB 4.4 bumps up the indices length but the real issue is that database
name that cloudron generates is big enough to make the whole thing exceed.
We will make a fix to make those db names shorter.
2020-08-17 09:44:06 -07:00
Girish Ramakrishnan 1453178693 settings.setAdmin -> setAdminLocation 2020-08-15 19:24:32 -07:00
Girish Ramakrishnan 510121bf54 remove support for hyphentated domains
this has not been used for a long time
2020-08-15 18:50:07 -07:00
Girish Ramakrishnan 2d607b394c Fix the exporting style 2020-08-15 18:19:01 -07:00
Girish Ramakrishnan bd12b0e441 These fields are now in the subdomains table 2020-08-15 17:25:51 -07:00
Girish Ramakrishnan 738b4e60fa notification: we do not retry update/backup every 4 hours anymore 2020-08-15 10:07:05 -07:00
Girish Ramakrishnan 1ae2f55c04 Remove verbose debug 2020-08-15 09:12:52 -07:00
Girish Ramakrishnan 2ebdf9673d Add VAAPI caps for transcoding 2020-08-14 18:48:53 -07:00
Girish Ramakrishnan 0427d790e5 Explain the command more clearly 2020-08-14 10:27:23 -07:00
Girish Ramakrishnan 90add7cf47 Add changes 2020-08-14 09:39:50 -07:00
Girish Ramakrishnan 26b1f8dfdb Do not automatically update to unstable release
fixes #726
2020-08-13 14:26:42 -07:00
Girish Ramakrishnan ba29889f54 remove IP nginx configuration that redirects to dashboard after activation
fixes #728
2020-08-13 14:10:17 -07:00
Girish Ramakrishnan 9d2284add7 Update mongodb to 4.2.8
Fixes #725
2020-08-13 11:32:48 -07:00
Girish Ramakrishnan dd44edde0a only clear backup cache if specific fields changed 2020-08-11 14:01:29 -07:00
Girish Ramakrishnan 885e90e810 add a todo 2020-08-11 12:57:37 -07:00
Girish Ramakrishnan 9cdf5dd0f3 backups: time the rotation and total as well 2020-08-11 10:28:11 -07:00
Girish Ramakrishnan df6e3eb1e6 Add deleteConcurrency setting 2020-08-11 09:14:09 -07:00
Girish Ramakrishnan 05026771e1 add memoryLimit, copyConcurrency, downloadConcurrency to backup config 2020-08-10 22:12:01 -07:00
Girish Ramakrishnan 7039108438 pass memory limit as argument to starttask.sh 2020-08-10 21:53:07 -07:00
Girish Ramakrishnan 02ee13cfb2 return empty array when listing 2020-08-10 21:32:54 -07:00
Girish Ramakrishnan 096e244252 Fix typo that causes aliases in lists to bounce
https://forum.cloudron.io/topic/2890/bug-with-mailing-lists-that-point-to-aliases
2020-08-10 17:49:27 -07:00
Girish Ramakrishnan bf5b7294a0 Add missing debugs 2020-08-10 14:54:37 -07:00
Girish Ramakrishnan a5da266643 groups: when listing, return members as well 2020-08-10 13:50:18 -07:00
Girish Ramakrishnan cf7bb49e15 More missing 5.5 changes 2020-08-10 10:16:09 -07:00
Girish Ramakrishnan 208b732bda yet more 5.5 changes 2020-08-10 10:07:50 -07:00
Girish Ramakrishnan c73d93b8bd more 5.5 changes 2020-08-10 10:05:47 -07:00
Girish Ramakrishnan 98a96eae2b Update mongodb
part of #725
2020-08-10 09:36:56 -07:00
Girish Ramakrishnan 2f9fe30c9d sftp: only mount data dirs that exist
when restoring, the platform starts first and the sftp container
goes and creates app data dirs with root permission. this prevents
the app restore logic from downloading the backup since it expects
yellowtent perm
2020-08-09 12:10:20 -07:00
Girish Ramakrishnan aeee8afc02 export database: fix async logic 2020-08-09 11:14:11 -07:00
Girish Ramakrishnan e85f0a4f52 Rename to box-task
this way we can do systemctl stop box*
2020-08-09 11:14:11 -07:00
Johannes Zellner da98649667 Ensure group listAllWitMembers also returns an ordered list 2020-08-09 11:34:53 +02:00
Girish Ramakrishnan 5ac08cc06b sftp: fix home directory path 2020-08-08 18:16:35 -07:00
Girish Ramakrishnan da72597dd3 Fix start/stop task scripts for ubuntu 16 2020-08-08 11:10:02 -07:00
Girish Ramakrishnan 1f1c94de70 Fix certificate ordering logic
* app certs set by user are always preferred
* If fallback, choose fallback certs. ignore others
* If LE, try to pick LE certs. Otherwise, provider fallback.

Fixes #724
2020-08-07 23:02:24 -07:00
Girish Ramakrishnan 60b3fceea6 reset-failed state of tasks during startup 2020-08-07 22:41:09 -07:00
Girish Ramakrishnan 5073809486 More 5.5.0 changes 2020-08-07 22:20:20 -07:00
Girish Ramakrishnan debd779cfd new public gpg key that doesn't expire
gpg --export admin@cloudron.io > releases.gpg
2020-08-07 22:17:30 -07:00
Girish Ramakrishnan 6b9454100e certs: remove caas backend 2020-08-07 17:58:27 -07:00
Girish Ramakrishnan 779ad24542 domains: remove caas backend, it is unused 2020-08-07 17:57:48 -07:00
Girish Ramakrishnan b94dbf5fa3 remove restricted fallback cert
this feature was never used. iirc, it was for managed hosting
2020-08-07 17:57:25 -07:00
Girish Ramakrishnan 45c49c9757 route53: verifyDnsConfig lists zones using old API
It should be using the listHostedZonesByName API but it was using the old
API (which has a 100 zone limitation) because it was using old credentials.
2020-08-07 09:54:02 -07:00
Girish Ramakrishnan 91288c96b1 s3: set queue size to 3
fixes #691
2020-08-07 00:28:00 -07:00
Girish Ramakrishnan f8e22a0730 Fix tests 2020-08-07 00:21:15 -07:00
Girish Ramakrishnan 114b45882a Set memory limit to 400M for tasks 2020-08-07 00:21:15 -07:00
Girish Ramakrishnan b1b6f70118 Kill all tasks on shutdown and startup
BindsTo will kill all the tasks when systemctl stop box is executed.
But when restarted, it keeps the tasks running. Because of this behavior,
we kill the tasks on startup and stop of the box code.
2020-08-06 23:47:40 -07:00
Girish Ramakrishnan 648d42dfe4 Empty debug prints as undefined for some reason 2020-08-06 23:23:56 -07:00
Girish Ramakrishnan 99f989c384 run apptask and backup task with a nice
A child process inherits whatever nice value is held by the parent at the time that it is forked
2020-08-06 16:46:39 -07:00
Girish Ramakrishnan 2112c7d096 sudo: remove the nice support 2020-08-06 16:44:35 -07:00
Girish Ramakrishnan ac63d00c93 run tasks as separate cgroup via systemd
this allows us to adjust the nice value and memory settings per task

part of #691
2020-08-06 16:43:14 -07:00
Girish Ramakrishnan e04871f79f pass log file as argument to task worker
initially, i thought i can hardcode the log file into taskworker.js
depending on the task type but for apptask, it's not easy to get the
appId from the taskId unless we introspect task arguments as well.
it's easier for now to pass it as an argument.
2020-08-05 00:46:34 -07:00
Girish Ramakrishnan 182c162dc4 hardcode logging of box code to box.log 2020-08-04 13:30:18 -07:00
Johannes Zellner 822b38cc89 Fallback to NOOP callback if not supplied 2020-08-04 14:32:01 +02:00
Girish Ramakrishnan d564003c87 backup cleaner: referenced backups must be counted as part of period
otherwise, we end up in a state where box backups keeps referencing
app backups and app backup cleanup is only performed on the remaining
app backups.
2020-08-03 21:22:27 -07:00
Girish Ramakrishnan 1b307632ab Use debug instead of console.* everywhere
No need to patch up console.* anymore

also removes supererror
2020-08-02 12:04:55 -07:00
Girish Ramakrishnan aa747cea85 update postgresl for pg_stat_statements,plpgsql extensions (loomio) 2020-08-02 11:36:42 -07:00
Girish Ramakrishnan f4a322478d cloudron.target is not needed 2020-08-01 20:00:20 -07:00
Girish Ramakrishnan d2882433a5 run backup uploader with a nice of 15
the gzip takes a lot of cpu processing and hogs the CPU. With a nice
level, we give other things higher priority.

An alternate idea that was explored was to use cpulimit. This is to
send SIGSTOP and SIGCONT periodically but this will not make use of the
CPU if it's idle (unlike nice).

Another idea is to use cgroups, but it's not clear how to use it with
the dynamic setup we have.

part of #691
2020-07-31 18:23:36 -07:00
Girish Ramakrishnan a94b175805 Add timing information for backups 2020-07-31 12:59:15 -07:00
Girish Ramakrishnan 37d81da806 do system checks once a day 2020-07-31 11:20:17 -07:00
Girish Ramakrishnan d089444441 db upgrade: stop containers only after exporting
we cannot export if the containers were nuked in the platform logic.
for this reason, move the removal near the place where they get started.
2020-07-30 15:28:53 -07:00
Girish Ramakrishnan b0d65a1bae rename startApps to markApps 2020-07-30 15:28:50 -07:00
Girish Ramakrishnan 16288cf277 better debug 2020-07-30 11:42:03 -07:00
Girish Ramakrishnan 7ddbabf781 Make the error message clearer 2020-07-30 11:29:43 -07:00
Girish Ramakrishnan fe35f4497b Fix two typos 2020-07-30 10:58:24 -07:00
Girish Ramakrishnan 625463f6ab export the database before upgrade
it's possible that
a) backups are completely disabled
b) skip backup option is selected when upgrading

in the above cases, the dump file is not generated and thus any addon
upgrade will fail. to fix, we dump the db fresh for database upgrades.
2020-07-30 10:23:08 -07:00
Johannes Zellner ff632b6816 Add more external ldap tests 2020-07-30 15:22:03 +02:00
Johannes Zellner fbc666f178 Make externalldap sync more robust 2020-07-30 15:08:01 +02:00
Girish Ramakrishnan d89bbdd50c Update to PostgreSQL 11 2020-07-29 21:54:05 -07:00
Girish Ramakrishnan 96f9aa39b2 add note on why we check for app updates separately 2020-07-29 20:27:06 -07:00
Girish Ramakrishnan 7330814d0f More 5.5 changes 2020-07-29 16:11:09 -07:00
Johannes Zellner 312efdcd94 Fix debug message 2020-07-29 20:38:46 +02:00
Girish Ramakrishnan 5db78ae359 Fix more usages of backup.intervalSecs 2020-07-29 11:25:59 -07:00
Girish Ramakrishnan 97967e60e8 remove yahoo from smtp test list 2020-07-29 11:25:59 -07:00
Johannes Zellner 9106b5d182 Avoid using extra /data dir for filemanager 2020-07-29 20:14:14 +02:00
Johannes Zellner 74bdb6cb9d Only mount app data volumes if localstorage is used 2020-07-29 19:58:41 +02:00
Johannes Zellner 0a44d426fa Explicitly mount all apps into the sftp container 2020-07-29 19:47:37 +02:00
Johannes Zellner e1718c4e8d If app.dataDir is set, first unmount from sftp before deleting on uninstall 2020-07-29 17:54:32 +02:00
Girish Ramakrishnan f511a610b5 backups: take a pattern instead of interval secs
part of #699
2020-07-28 21:54:56 -07:00
Girish Ramakrishnan 4d5715188d Increase invite link expiry to a week 2020-07-28 14:19:19 -07:00
Johannes Zellner 2ea21be5bd Add basic backup check route tests 2020-07-28 17:23:21 +02:00
Johannes Zellner 5bb0419699 Add backup check route
Part of #719
2020-07-28 17:18:50 +02:00
Johannes Zellner a8131eed71 Run initial backup configuration check only after activation
Part of #719
2020-07-28 17:12:38 +02:00
Girish Ramakrishnan ed09c06ba4 Add option to remove mailbox data
Fixes #720
2020-07-27 22:55:09 -07:00
Girish Ramakrishnan 3c59a0ff31 make it clear it is exported for testing 2020-07-27 22:07:25 -07:00
Girish Ramakrishnan a6d24b3e48 postgresql: add btree_gist,postgres_fdw extensions for gitlab 2020-07-24 22:30:45 -07:00
Girish Ramakrishnan 060135eecb Next release is 5.5 2020-07-24 09:33:53 -07:00
Johannes Zellner ef296c24fe Mount data custom app data location specifically into sftp addon
Fixes #722
2020-07-24 15:43:26 +02:00
Girish Ramakrishnan 707aaf25ec Add note on underscore in usernames 2020-07-23 16:29:54 -07:00
Girish Ramakrishnan 7edeb0c358 nginx displays version in stderr 2020-07-22 17:57:55 -07:00
Girish Ramakrishnan e516af14b2 typo 2020-07-22 17:53:04 -07:00
Girish Ramakrishnan 4086f2671d Disable ldap/directory config/2fa in demo mode 2020-07-22 16:18:22 -07:00
Girish Ramakrishnan 23c4550430 Update postgresql addon to have citext extension for loomio 2020-07-22 08:29:44 -07:00
Johannes Zellner 31d25cd6be Add 5.4.1 changes 2020-07-19 21:11:05 +02:00
Johannes Zellner 07b3c7a245 Use sftp addon with fixed symlinks 2020-07-18 19:27:02 +02:00
Girish Ramakrishnan a00b7281a7 Fixup changelog 2020-07-17 10:43:22 -07:00
Girish Ramakrishnan ddeee0c970 Add note that links expire in 24 hours 2020-07-16 15:17:51 -07:00
Johannes Zellner 8aad71efd0 Add more feature flags 2020-07-16 18:14:25 +02:00
Johannes Zellner 2028f6b984 Do not reassign ubunt_codename in base image init 2020-07-16 16:42:15 +02:00
Girish Ramakrishnan bff4999d27 mail: add mailbox count route 2020-07-15 15:48:30 -07:00
Johannes Zellner d429015f83 Add more 3.4.0 changes 2020-07-15 14:57:06 +02:00
Johannes Zellner e2628e2d43 Use latest filemanager addon
Fixes dot- and json-files
2020-07-14 17:16:41 +02:00
Girish Ramakrishnan 05dcbee7e3 backups: add b2 provider
part of #508
2020-07-13 14:52:35 -07:00
Johannes Zellner a81919262e Use addon with chown functionality 2020-07-13 18:48:42 +02:00
Girish Ramakrishnan b14b5f141b Hide nginx version 2020-07-13 09:27:57 -07:00
Girish Ramakrishnan 1259d11173 Add back provider field into getStatus 2020-07-13 08:46:05 -07:00
Johannes Zellner 0a7b132be8 Remove or increase timeouts for filemanager 2020-07-13 17:05:22 +02:00
Girish Ramakrishnan ed9210eede Add mandatory 2FA flag
part of #716
2020-07-10 10:25:04 -07:00
Girish Ramakrishnan 9ee6aa54c6 avatar is not part of the profile lock
this is because avatar is not exposed via LDAP anyways. it's purely
a personal dashboard thing.
2020-07-10 09:43:42 -07:00
Girish Ramakrishnan 7cfc455cd3 make tests pass again
also disable column statistics on ubuntu 20
2020-07-10 09:33:35 -07:00
Johannes Zellner a481ceac8c Allow larger file uploads for filemanager 2020-07-10 18:23:55 +02:00
Girish Ramakrishnan 8c7eff4e24 user: add routes to set/clear avatar 2020-07-10 07:23:38 -07:00
Girish Ramakrishnan c6c584ff74 user: move avatar handling into model code 2020-07-10 07:01:15 -07:00
Johannes Zellner ba50eb121d Use new sftp addon 2020-07-10 14:13:16 +02:00
Johannes Zellner aa8ebbd7ea Add filemanager proxy routes 2020-07-10 14:10:52 +02:00
Girish Ramakrishnan 64bc9c6dbe disable profile view for all users to avoid confusion 2020-07-09 21:54:09 -07:00
Girish Ramakrishnan bba9963b7c Add directoryConfig feature flag
Fixes #704
2020-07-09 21:51:22 -07:00
Girish Ramakrishnan 6ea2aa4a54 return profileLocked in config route
part of #704
2020-07-09 17:28:44 -07:00
Girish Ramakrishnan 3c3f81365b add route to get/set directory config
part of #704
2020-07-09 17:12:07 -07:00
Girish Ramakrishnan 3adeed381b setup account based on directory config
part of #704
2020-07-09 16:39:34 -07:00
Girish Ramakrishnan 0f5b7278b8 add directory config setting
part of #704
2020-07-09 16:02:58 -07:00
Girish Ramakrishnan f94ff49fb9 users: replace modifiedAt with ts 2020-07-09 16:02:49 -07:00
Girish Ramakrishnan d512a9c30d rename function 2020-07-09 16:02:43 -07:00
Girish Ramakrishnan 0c5113ed5b email is never used in account setup 2020-07-09 15:37:35 -07:00
Girish Ramakrishnan 2469f4cdff rename function to sendPasswordResetByIdentifier 2020-07-09 15:37:35 -07:00
Girish Ramakrishnan 9c53bfb7fb Do not show LDAP logs, it spams a lot 2020-07-07 11:16:47 -07:00
Girish Ramakrishnan 8b8144588d list must search members 2020-07-05 11:44:46 -07:00
Girish Ramakrishnan 77553da4c1 mail: add search param for mailbox and mailing list api 2020-07-05 11:23:53 -07:00
Girish Ramakrishnan cbcf943691 mail: parameterize the query 2020-07-05 10:48:08 -07:00
Girish Ramakrishnan 725a19e5b5 mail: Add pagination to lists API
Fixes #712
2020-07-05 10:48:04 -07:00
Girish Ramakrishnan f9115f902a Do not send alive status
we used to do this for managed hosting to track scaling but we don't
need this info anymore
2020-07-03 19:13:27 -07:00
Girish Ramakrishnan e4faf26d74 5.3.4 changes
(cherry picked from commit 77785097c1)
2020-07-03 14:23:20 -07:00
Girish Ramakrishnan 1c96fbb533 Fixes for tests 2020-07-03 13:47:56 -07:00
Girish Ramakrishnan 3dc163c33d database: rework connection logic 2020-07-03 13:14:00 -07:00
Girish Ramakrishnan edae94cf2e Bump max_connection for postgres addon to 200 2020-07-02 15:47:26 -07:00
Girish Ramakrishnan d1ff8e9d6b Fix crash when mysql crashes 2020-07-02 15:10:05 -07:00
Girish Ramakrishnan 70743bd285 database: Fix event emitter warning
the connection object gets reused after release. this means that we keep
attaching the 'error' event and not unlistening.

--trace-warnings can be added to box.service to get the stack trace
2020-07-02 12:00:56 -07:00
Johannes Zellner 493f1505f0 Check also for mountpoint on filesystem with external disk 2020-07-02 19:08:27 +02:00
Girish Ramakrishnan 007e3b5eef Add changes 2020-07-01 14:29:40 -07:00
Johannes Zellner d9bf6c0933 also support uniqueMember property next to member for ldap groups 2020-07-01 17:08:17 +02:00
Johannes Zellner 324344d118 Reusue the single correct ldap.createClient call also in auth 2020-07-01 14:59:26 +02:00
Johannes Zellner 5cb71e9443 No need to return externalLdapConfig in getClient() 2020-07-01 14:52:11 +02:00
Johannes Zellner cca19f00c5 Fallback to mailPrimaryAddress in ldap sync 2020-07-01 14:34:41 +02:00
Girish Ramakrishnan 6648f41f3d nginx: [warn] the "ssl" directive is deprecated, use the "listen ... ssl" directive 2020-06-30 16:00:52 -07:00
Girish Ramakrishnan c1e6b47fd6 Fix sogo aliases
Fixes cloudron/sogo#18
2020-06-30 14:29:50 -07:00
Girish Ramakrishnan 0f103ccce1 Add ping capability (for statping) 2020-06-30 07:40:17 -07:00
Girish Ramakrishnan bc6e652293 5.3.3 changes 2020-06-29 19:52:08 -07:00
Girish Ramakrishnan 85b4f2dbdd print sudo command to check failures 2020-06-29 14:03:34 -07:00
Girish Ramakrishnan d47b83a63b Package lock mystery 2020-06-29 14:03:15 -07:00
Girish Ramakrishnan b2e9fa7e0d aschema: dd servicesConfigJson 2020-06-26 15:48:39 -07:00
Girish Ramakrishnan a9fb444622 Use nginx 1.18 for security fixes 2020-06-26 14:57:53 -07:00
Girish Ramakrishnan 33ba22a021 Put this in 5.3.2 itself 2020-06-26 10:41:32 -07:00
Girish Ramakrishnan 57de0282cd remove provider from trackBeginSetup 2020-06-26 09:55:39 -07:00
Girish Ramakrishnan 8568fd26d8 Fix failing test 2020-06-26 09:48:10 -07:00
Girish Ramakrishnan 84f41e08cf Add mlock capability to manifest (for vault app) 2020-06-26 09:27:35 -07:00
Johannes Zellner a96da20536 TODO is done for filesystem backend moutnpoint check 2020-06-26 17:57:26 +02:00
Johannes Zellner 5199a9342e Add missing ldap client error handling 2020-06-26 17:55:42 +02:00
Girish Ramakrishnan 893ecec0fa redis: Set maxmemory and maxmemory-policy 2020-06-26 08:54:47 -07:00
Girish Ramakrishnan e3da6419f5 Add 5.3.2 changes 2020-06-26 08:48:01 -07:00
Girish Ramakrishnan 0750d2ba50 More changes 2020-06-25 16:48:11 -07:00
Girish Ramakrishnan f1fcb65fbe Do not install sshfs. user will install it if they want
we don't use sshfs anywhere in our code ourselves
2020-06-25 12:21:49 -07:00
Girish Ramakrishnan 215aa65d5a Fix provider usage
* do not send to appstore anymore
* do not set in getStatus/getConfig
* provider is not needed when registering cloudron
2020-06-25 11:20:05 -07:00
Girish Ramakrishnan 85f67c13da remove unused registerWithLicense 2020-06-25 11:11:52 -07:00
Girish Ramakrishnan 6dcc478aeb add to changes 2020-06-25 09:20:37 -07:00
Johannes Zellner 3f2496db6f Support self-signed certs for external ldap/ad 2020-06-25 17:45:59 +02:00
Johannes Zellner 612f79f9e0 Copy over changes for 5.3.1 2020-06-25 14:22:44 +02:00
Johannes Zellner 90fb1cd735 We also need enableBackup property for app listing api 2020-06-25 12:31:00 +02:00
Girish Ramakrishnan 7c24d9c6c6 Give graphite more memory 2020-06-22 09:55:01 -07:00
Johannes Zellner 60f1b2356a Also make nfs storage provider same as cifs and sshfs 2020-06-22 15:51:05 +02:00
Johannes Zellner 0b8f21508f Add more changes 2020-06-22 12:04:34 +02:00
Johannes Zellner ae128c0fa4 If no appstore account is setup restrict features to free plan 2020-06-22 12:02:10 +02:00
Girish Ramakrishnan 1b4ec9ecf9 Update changes 2020-06-18 10:25:45 -07:00
Girish Ramakrishnan b0ce0b61d6 logging: fix crash when router errors 2020-06-18 09:27:09 -07:00
Girish Ramakrishnan e1ffdaddfa Fix timeout issues in postgresql and mysql addon 2020-06-17 16:39:30 -07:00
Johannes Zellner af8344f482 remove unused requires 2020-06-16 14:37:06 +02:00
Johannes Zellner 7dc2596b3b Ensure we support pre 5.3 Cloudron installation 2020-06-16 14:10:14 +02:00
Johannes Zellner 0109956fc2 do not rely on some argument passed through for infraversion base path 2020-06-16 14:09:55 +02:00
Johannes Zellner 945fe3f3ec Do not spam install logs with nodejs tarball contents 2020-06-16 13:58:23 +02:00
Johannes Zellner 9c868135f3 app sso flag is not restricted now 2020-06-16 13:09:06 +02:00
Girish Ramakrishnan 5be288023b update mail container to record separator and spam folder 2020-06-15 13:50:46 -07:00
Girish Ramakrishnan a03f97186c Make mail auth case insensitive 2020-06-15 09:58:55 -07:00
Johannes Zellner 0aab891980 Support nginx logs 2020-06-15 17:30:16 +02:00
Johannes Zellner 5268d3f57d Fix test for systems without swap 2020-06-15 16:06:54 +02:00
Girish Ramakrishnan 129cbb5beb backups: fix cleanup
The various changes are:
* Latest backup is always kept for box and app backups
* If the latest backup is part of the policy, it is not counted twice
* Latest backup comes into action only when all backups are outside the retention policy
* For uninstalled apps, latest backup is not preserved
* This way the latest backup of apps that are not referenced in box backup is preserved.
  (for example, for stopped apps)

fixes #692
2020-06-14 22:06:00 -07:00
Girish Ramakrishnan 2601d2945d Fix backup tests 2020-06-14 14:01:01 -07:00
Girish Ramakrishnan e3829eb24b typo 2020-06-14 14:00:29 -07:00
Girish Ramakrishnan f6cb1a0863 backups: query using identifier instead of type
this allows us to move the enums into backups.js instead of backupdb.js
2020-06-14 12:27:41 -07:00
Girish Ramakrishnan 4f964101a0 add identifier to backups table 2020-06-14 11:39:44 -07:00
Girish Ramakrishnan f6dcba025f auditSource is not used in the worker 2020-06-14 09:09:41 -07:00
Johannes Zellner d6ec65d456 Do not remove alternateDomains to allow apps view filter to work 2020-06-14 13:39:15 +02:00
Girish Ramakrishnan 65d8074a07 Fix failing backup test 2020-06-12 12:58:11 -07:00
Girish Ramakrishnan e3af61ca4a Fix failing test 2020-06-12 12:52:32 -07:00
Girish Ramakrishnan a58f1268f0 mail: Add Auto-Submitted header to NDRs 2020-06-11 19:48:37 -07:00
Girish Ramakrishnan 41eacc4bc5 postgresql: Add unaccent extension 2020-06-11 09:53:53 -07:00
Girish Ramakrishnan aabb9dee13 Fix transaction rollback logic 2020-06-11 09:50:49 -07:00
Girish Ramakrishnan c855d75f35 remove mkdirp use
node 10.12 has { recursive: true }
2020-06-11 08:27:48 -07:00
Girish Ramakrishnan 8f5cdcf439 backups: some logs for debugging 2020-06-10 23:00:23 -07:00
Girish Ramakrishnan 984559427e update manifest format to 5.3.0 2020-06-09 11:35:54 -07:00
Johannes Zellner 89494ced41 Check for sshfs and cifs backup backends, if they are mounted 2020-06-08 17:46:52 +02:00
Johannes Zellner ef764c2393 Merge sshfs.js into filesystem.js 2020-06-08 17:08:26 +02:00
Johannes Zellner 8624e2260d add storage api to make preflight checks
Currently there is only disk space checking but sshfs and cifs need
mount point checking as well
2020-06-08 16:25:05 +02:00
Johannes Zellner aa011f4add add ldap group tests and fixes for the found issues 2020-06-07 13:49:01 +02:00
Girish Ramakrishnan 3df61c9ab8 do not automatically update unstable updates
part of #698
2020-06-05 16:26:23 -07:00
Girish Ramakrishnan a4516776d6 make canAutoupdateApp take updateInfo object
part of #698
2020-06-05 16:06:37 -07:00
Girish Ramakrishnan 54d0ade997 curl uses -s and not -q 2020-06-05 13:50:40 -07:00
Johannes Zellner 3557fcd129 Add sshfs quirks to shared code in filesytstem.js 2020-06-05 13:45:25 +02:00
Johannes Zellner 330b4a613c Retrieve the backupPath from the storage provider itself 2020-06-05 13:27:18 +02:00
Johannes Zellner 7ba3412aae Add some sshfs config tests 2020-06-05 12:43:09 +02:00
Johannes Zellner 6f60495d4d Initial version of sshfs storage backend 2020-06-05 11:39:51 +02:00
Johannes Zellner 0b2eb8fb9e Sync users into groups
This does not yet remove users from groups

Part of #685
2020-06-05 11:28:57 +02:00
Johannes Zellner 48af17e052 Groups are lowercase on Cloudron 2020-06-05 10:13:19 +02:00
Johannes Zellner b7b1055530 Avoid the pyramid 2020-06-05 09:26:52 +02:00
Johannes Zellner e7029c0afd Remove unsused and poorly named groups.getGroups() API 2020-06-05 09:24:00 +02:00
Johannes Zellner cba3674ac0 Stop ldap syncing if we hit some internal error 2020-06-05 09:03:30 +02:00
Girish Ramakrishnan 865a549885 say connected 2020-06-04 11:27:11 -07:00
Girish Ramakrishnan 50dcf827a5 remove console.error use in many places
the backtraces just flood the logs

apphealthtask: remove console.error
remove spurious console.dir
cleanup scheduler error logging
2020-06-04 11:21:56 -07:00
Girish Ramakrishnan f5fb582f83 log status and message in morgan
connect lastmile does not forward final handler to express anymore.
otherwise, express logs using console.error()
https://github.com/expressjs/express/issues/2263
2020-06-04 09:17:58 -07:00
Girish Ramakrishnan dbba502f83 remove message from debug 2020-06-04 09:17:58 -07:00
Girish Ramakrishnan aae49f16a2 database: do no reconnect in query 2020-06-04 09:17:58 -07:00
Girish Ramakrishnan 45d5f8c74d make rollback return an error
fixes #690
2020-06-04 09:17:58 -07:00
Girish Ramakrishnan 6cfd64e536 database: do not crash if connection errors
Part of #690
2020-06-04 09:17:58 -07:00
Girish Ramakrishnan c5cc404b3e do not retry here
Part of #690
2020-06-04 09:17:58 -07:00
Johannes Zellner 42cbcc6ce3 groups.create() now needs source argument 2020-06-04 14:20:05 +02:00
Johannes Zellner 812bdcd462 Fix groups test by ensuring we order by name 2020-06-04 14:03:06 +02:00
Johannes Zellner f275409ee8 Fix cloudron api tests 2020-06-04 13:55:47 +02:00
Johannes Zellner 8994ac3727 Fix backup retention tests 2020-06-04 13:43:25 +02:00
Johannes Zellner 7c5ff5e4d5 Create user groups for ldap groups 2020-06-04 13:26:13 +02:00
Johannes Zellner c5e84d5469 Add source property to userGroups 2020-06-04 13:25:55 +02:00
Johannes Zellner c143450dc6 WIP 2020-06-04 12:59:27 +02:00
Johannes Zellner 07b95c2c4b Add groups.getByName() 2020-06-04 12:48:35 +02:00
Johannes Zellner c30734f7f3 Show in the logs if group sync is disabled 2020-06-04 12:40:28 +02:00
Johannes Zellner 91f506c17b Explicitly enable/disable ldap group sync 2020-06-04 12:28:31 +02:00
Girish Ramakrishnan 7a17695ad5 Retry in 10 seconds to not make things worse
Part of #690
2020-06-03 16:05:48 -07:00
Girish Ramakrishnan f5076c87d4 add to changes 2020-06-03 13:52:53 -07:00
Girish Ramakrishnan a47d6e1f3a cloudron-setup: --provider is dead
Long live --provider

Part of #693
2020-06-03 13:47:30 -07:00
Girish Ramakrishnan f6ff1abb00 cloudron-setup: remove --license arg. unused 2020-06-03 13:16:39 -07:00
Johannes Zellner 386aaf6470 Initial code to fetch LDAP groups during sync 2020-06-03 22:12:38 +02:00
Johannes Zellner 2b3c4cf0ff avatar blob now comes in only via branding api calls 2020-06-02 15:13:50 +02:00
Girish Ramakrishnan b602e921d0 better error message if domains exists 2020-06-01 16:11:02 -07:00
Girish Ramakrishnan 2fc3cdc2a2 remove superfluous debug 2020-06-01 09:40:56 -07:00
Girish Ramakrishnan e2cadbfc30 Fix uniqueness constraint in app passwords table
Fixes #688
2020-05-30 13:25:29 -07:00
Girish Ramakrishnan 3ffa935da7 Revert "part focal support"
This reverts commit 7d36533524.

not ready yet
2020-05-30 10:58:28 -07:00
Girish Ramakrishnan 5f539e331a 5.3.0 changes 2020-05-30 09:45:24 -07:00
Girish Ramakrishnan 356d0fabda Add note that pattern must match dashboard code 2020-05-30 09:44:33 -07:00
Girish Ramakrishnan 122ec75cb6 Fix links 2020-05-29 19:10:42 -07:00
Girish Ramakrishnan a3a48e1a49 poll for updates a bit more often 2020-05-29 13:39:16 -07:00
Girish Ramakrishnan 4ede765e1f typo: memoryLimit -> memory 2020-05-29 13:29:01 -07:00
Girish Ramakrishnan 4fa181b346 re-use the latest backup id for non-backupable apps
for stopped apps, as an example
2020-05-28 14:16:38 -07:00
Johannes Zellner 4f76d91ae9 Add backup_config settings API tests 2020-05-28 21:42:25 +02:00
Girish Ramakrishnan 20d1759fa5 Run update checker on stopped apps, we just don't update them 2020-05-28 12:41:51 -07:00
Girish Ramakrishnan 433e783ede do not allow backup, import, update in stopped state 2020-05-28 12:41:51 -07:00
Johannes Zellner 47f47d916d Fixup tests 2020-05-28 21:05:21 +02:00
Johannes Zellner b31ac7d1fd Revert backup policy fallback and check in rest api
Check is now in proper location at backups.testConfig()
2020-05-28 20:44:44 +02:00
Johannes Zellner ea47fb7305 Properly check for backup policy in testConfig() 2020-05-28 20:44:44 +02:00
Girish Ramakrishnan 82170f8f1b Fix failing test 2020-05-28 11:04:39 -07:00
Girish Ramakrishnan acb2655f58 rename variable (it ensures backup and may not actually backup) 2020-05-28 11:03:49 -07:00
Girish Ramakrishnan b1464517e6 centralize all the cron patterns in one place 2020-05-28 11:01:46 -07:00
Girish Ramakrishnan 151e6351f6 add couple of 5.2 changes 2020-05-28 09:37:57 -07:00
Johannes Zellner 154f768281 Forgot .length 2020-05-28 16:44:45 +02:00
Johannes Zellner 90c857e8fc Further validate retentionPolicy api input 2020-05-28 16:27:07 +02:00
Johannes Zellner 7a3efa2631 Ensure we get a proper retention policy for backups 2020-05-28 16:26:21 +02:00
Girish Ramakrishnan 38cc767f27 move up the backup cron to not overlap auto-updates 2020-05-27 23:04:04 -07:00
Girish Ramakrishnan e1a718c78f remove redundant call to canBackupApp 2020-05-27 22:48:48 -07:00
Girish Ramakrishnan 32a4450e5e 5.2.4 changes
(cherry picked from commit 2dc7342f09)
2020-05-27 22:35:30 -07:00
Girish Ramakrishnan fca3f606d2 Do not backup stopped apps 2020-05-27 21:04:01 -07:00
Girish Ramakrishnan 4a0a934a76 start using vhost style for accessing s3 style storage
if bucket name has a '.', accept self-signed

fixes #680
2020-05-27 17:50:37 -07:00
Girish Ramakrishnan f7c406bec9 s3: bucket name cannot contain _ or capitals or .
we can make it more elaborate, but not sure if it's needed

https://blogs.easydynamics.com/2016/10/24/aws-s3-bucket-name-validation-regex/
2020-05-27 17:01:42 -07:00
Girish Ramakrishnan f4807a6354 update many node modules 2020-05-27 16:52:22 -07:00
Girish Ramakrishnan 0960008b7b 5.2.4 changes
(cherry picked from commit 4267f5ea0a)
2020-05-26 17:07:03 -07:00
Girish Ramakrishnan 04a1aa38b4 Add CIFS as storage provider
part of #686
2020-05-26 15:31:45 -07:00
Girish Ramakrishnan f84622efa1 fs: add create/unlink tests 2020-05-26 15:31:41 -07:00
Girish Ramakrishnan f6c4614275 Do not restart stopped apps
(cherry picked from commit 2e76b8bed9)
2020-05-26 07:54:35 -07:00
Girish Ramakrishnan 7d36533524 part focal support
part of #684
2020-05-25 19:49:15 -07:00
Girish Ramakrishnan 5cd3df4869 better nginx config for higher loads 2020-05-25 15:25:00 -07:00
Girish Ramakrishnan b0480f48f3 Add changes 2020-05-24 20:12:19 -07:00
Girish Ramakrishnan 2e820c343a remove meaningless debug 2020-05-24 20:11:03 -07:00
Girish Ramakrishnan ce927a2247 Set dmode in tar extract 2020-05-24 20:08:17 -07:00
Girish Ramakrishnan ae810d59e9 mail: fix crash in audit logs 2020-05-24 18:50:10 -07:00
Girish Ramakrishnan 1438ee52a1 import: fix crash because encryption is unset 2020-05-24 18:42:04 -07:00
Girish Ramakrishnan de4b3e55fa Use apps.getAll so that app.fqdn is valid 2020-05-24 18:21:35 -07:00
Girish Ramakrishnan d2cd78c5cb more debug() removal 2020-05-24 12:30:48 -07:00
Girish Ramakrishnan d000719fa2 app health monitor is too verbose 2020-05-24 11:43:17 -07:00
Girish Ramakrishnan efea4ed615 more debug() removal 2020-05-24 11:35:31 -07:00
Girish Ramakrishnan 67a931c4b8 Remove verbose logs 2020-05-24 11:33:53 -07:00
Girish Ramakrishnan bdcc5c0629 Mbps -> MBps
Fixes #682
2020-05-23 13:31:23 -07:00
Girish Ramakrishnan d113cfc0ba add comment on how often du value is stored 2020-05-22 20:06:45 -07:00
Girish Ramakrishnan 4a3ab50878 5.2.1 changes 2020-05-22 18:49:28 -07:00
Girish Ramakrishnan b39261c8cf remove extra $ 2020-05-22 16:56:01 -07:00
Girish Ramakrishnan 7efb57c8da restart apps on addon container change
when the IP changes on addon container re-create, the apps don't
detect this (maybe there is some large DNS cache timeout in docker)
2020-05-22 16:45:03 -07:00
Girish Ramakrishnan 90c24cf356 add cleanup policy test 2020-05-21 14:30:21 -07:00
Girish Ramakrishnan 54abada561 backups: add progressCallback to cleanup funcs 2020-05-21 13:46:16 -07:00
Girish Ramakrishnan f1922660be add a new line 2020-05-21 10:57:57 -07:00
Girish Ramakrishnan 795e3c57da Add a header for encrypted backup files
this is required to identify old backups and new backups for decryption
2020-05-20 22:44:26 -07:00
Girish Ramakrishnan 3f201464a5 Fix bug where SRS translation was done on the main domain instead of mailing list domain 2020-05-20 21:55:48 -07:00
Girish Ramakrishnan 8ac0be6bb5 Update postgresql for schema ownership fix 2020-05-20 16:44:32 -07:00
Johannes Zellner 130805e7bd Add changes 2020-05-19 14:59:28 +02:00
Girish Ramakrishnan b8c7357fea redis: if container inactive, return stopped status 2020-05-18 14:43:23 -07:00
Girish Ramakrishnan 819f8e338f stop app now stops it's services as well 2020-05-18 14:33:07 -07:00
Girish Ramakrishnan 9569e46ff8 use docker.restart instead of start/stop since it is atomic 2020-05-18 13:35:42 -07:00
Girish Ramakrishnan b7baab2d0f restore: set encryption to null 2020-05-18 09:07:18 -07:00
Girish Ramakrishnan e2d284797d set HOME explicity when calling migrate script 2020-05-17 21:50:50 -07:00
Girish Ramakrishnan a3ac343fe2 installer: print from and to versions 2020-05-17 21:34:39 -07:00
Girish Ramakrishnan dadde96e41 remove login events from addons
more often then not this just spams the eventlog
2020-05-15 21:40:34 -07:00
Girish Ramakrishnan 99475c51e8 fix encryption of 0-length files 2020-05-15 16:05:12 -07:00
Girish Ramakrishnan cc9b4e26b5 use done event to signal write success (just like in extract) 2020-05-15 15:24:12 -07:00
Girish Ramakrishnan 32f232d3c0 destroy input stream on error 2020-05-15 15:21:24 -07:00
Girish Ramakrishnan 235047ad0b bind to source stream error event immediately
download() is async and the source stream error is missed
2020-05-15 14:54:05 -07:00
Girish Ramakrishnan 228f75de0b better error messages 2020-05-15 14:35:19 -07:00
Girish Ramakrishnan 2f89e7e2b4 drop NET_RAW since this allows packet sniffing
this however breaks ping
2020-05-15 12:47:36 -07:00
Girish Ramakrishnan 437f39deb3 More changes 2020-05-15 09:16:24 -07:00
Girish Ramakrishnan 59582f16c4 skip validation in the route 2020-05-14 21:45:13 -07:00
Girish Ramakrishnan af9e3e38ce apply backup retention policy
part of #441
2020-05-14 21:31:24 -07:00
Girish Ramakrishnan d992702b87 rename to keepWithinSecs
part of #441
2020-05-14 16:45:28 -07:00
Girish Ramakrishnan 6a9fe1128f move retentionSecs inside retentionPolicy
part of #441
2020-05-14 16:33:29 -07:00
Johannes Zellner 573da29a4d Once upon a time where settings worked 2020-05-14 23:35:03 +02:00
Johannes Zellner 00cff1a728 Mention that SECRET_PLACEHOLDER is also used in dashboard client.js 2020-05-14 23:04:08 +02:00
Johannes Zellner 9bdeff0a39 Always use constants.SECRET_PLACEHOLDER 2020-05-14 23:02:02 +02:00
Girish Ramakrishnan a1f263c048 stash the backup password in filesystem for safety
we will add a release note asking the user to nuke it
2020-05-14 12:59:37 -07:00
Girish Ramakrishnan 346eac389c bind ui is hidden for this release 2020-05-14 11:57:12 -07:00
Johannes Zellner f52c16b209 Ensure encryption property on backup config always exists 2020-05-14 20:22:10 +02:00
Girish Ramakrishnan 4faf880aa4 Fix crash with unencrypted backups 2020-05-14 11:18:41 -07:00
Girish Ramakrishnan f417a49b34 Add encryptionVersion to backups
this will identify the old style backups and warn user that a restore
doesn't work anymore
2020-05-13 22:37:02 -07:00
Girish Ramakrishnan 66fd713d12 rename version to packageVersion 2020-05-13 21:55:50 -07:00
Girish Ramakrishnan 2e7630f97e remove stale logs 2020-05-13 19:23:04 -07:00
Girish Ramakrishnan 3f10524532 cleanup cache file to start encrypted rsync backups afresh 2020-05-13 16:35:13 -07:00
Johannes Zellner 51f9826918 Strip quotes for TXT records on name.com
The docs and support claim quotes are needed, but the actual API usage
shows otherwise. We do this to not break users, but ideally name.com
gives a correct and clear answer
2020-05-14 01:03:10 +02:00
Girish Ramakrishnan f5bb76333b do hmac validation on filename iv as well
also, pass encryption object instead of config
2020-05-13 10:11:07 -07:00
Girish Ramakrishnan 4947faa5ca update mail container 2020-05-12 23:19:31 -07:00
Girish Ramakrishnan 101dc3a93c s3: do not retry when testing config 2020-05-12 22:45:01 -07:00
Girish Ramakrishnan bd3ee0fa24 add changes 2020-05-12 22:00:05 -07:00
Girish Ramakrishnan 2c52668a74 remove format validation in provider config 2020-05-12 22:00:01 -07:00
Girish Ramakrishnan 03edd8c96b remove max_old_space_size
we have limited understanding of this option
2020-05-12 20:14:35 -07:00
Girish Ramakrishnan 37dfa41e01 Add hmac to the file data
https://stackoverflow.com/questions/10279403/confused-how-to-use-aes-and-hmac
https://en.wikipedia.org/wiki/Padding_oracle_attack

part of #579
2020-05-12 19:59:06 -07:00
Girish Ramakrishnan ea8a3d798e create encryption keys from password during app import & restore 2020-05-12 15:53:18 -07:00
Girish Ramakrishnan 1df94fd84d backups: generate keys from password
this also removes storage of password from db

part of #579
2020-05-12 15:14:51 -07:00
Girish Ramakrishnan 5af957dc9c add changes
part of #579
2020-05-12 10:56:07 -07:00
Girish Ramakrishnan 21073c627e rename backup key to password
Fixes #579
2020-05-12 10:55:10 -07:00
Girish Ramakrishnan 66cdba9c1a remove chat link in readme 2020-05-12 10:21:21 -07:00
Girish Ramakrishnan 56d3b38ce6 read/write iv in the encrypted files
part of #579
2020-05-11 22:35:25 -07:00
Girish Ramakrishnan 15d0275045 key must atleast be 8 chars
part of #579
2020-05-11 16:11:41 -07:00
Girish Ramakrishnan 991c1a0137 check if manifest property is present in network response 2020-05-11 14:52:55 -07:00
Girish Ramakrishnan 7d549dbbd5 logrotate: add some comments 2020-05-11 14:38:50 -07:00
Johannes Zellner e27c5583bb Apps without dockerImage cannot be auto-updated 2020-05-11 23:20:17 +02:00
Girish Ramakrishnan 650c49637f logrotate: Add turn service logs 2020-05-11 13:14:52 -07:00
Girish Ramakrishnan eb5dcf1c3e typo 2020-05-11 11:58:14 -07:00
Girish Ramakrishnan ed2b61b709 Add to changes 2020-05-10 15:35:06 -07:00
Girish Ramakrishnan 41466a3018 No need to poll every hour for updates! 2020-05-06 18:58:35 -07:00
Girish Ramakrishnan 2e130ef99d Add automatic flag for update checks
The appstore can then known if a user clicked the check for updates
button manually or if it was done by the automatic updater.

We will fix appstore so that updates are always provided for manual checks.
automatic updates will follow our roll out plan.

We do have one issue that the automatic update checker will reset the manual
updates when it runs, but this is OK.
2020-05-06 18:57:59 -07:00
Girish Ramakrishnan a96fb39a82 mail relay: fix delivery event log 2020-05-05 20:34:45 -07:00
Girish Ramakrishnan c9923c8d4b spam: large emails were not scanned 2020-05-05 15:23:27 -07:00
Girish Ramakrishnan 74b0ff338b Disallow cloudtorrent in demo mode 2020-05-04 14:56:10 -07:00
Girish Ramakrishnan dcaccc2d7a add redis status
part of #671
2020-05-03 19:46:07 -07:00
Johannes Zellner d60714e4e6 Use webmaster@ instead of support@ as LetsEncrypt fallback 2020-05-03 11:02:18 +02:00
Girish Ramakrishnan d513d5d887 appstore: Better error messages 2020-05-02 18:30:44 -07:00
Girish Ramakrishnan 386566fd4b Fcf: ix crash when no email provide with global key 2020-05-02 18:06:21 -07:00
Girish Ramakrishnan 3357ca76fe specify the invalid bind name in error message 2020-05-02 11:07:58 -07:00
Girish Ramakrishnan a183ce13ee put the status code in the error message 2020-04-30 09:24:22 -07:00
Girish Ramakrishnan e9d0ed8e1e Add binds support to containers 2020-04-29 22:51:46 -07:00
Girish Ramakrishnan 66f66fd14f docker: clean up volume API 2020-04-29 21:28:49 -07:00
Girish Ramakrishnan b49d30b477 Add OVH Object Storage backend 2020-04-29 12:47:57 -07:00
Girish Ramakrishnan 73d83ec57e Ensure stopped apps are getting backed up 2020-04-29 12:05:01 -07:00
Girish Ramakrishnan efb39fb24b refactor for addon/service/container consistency
addon - app manifest thing. part of app lifecycle
services - implementation of addon (may have containers assoc)
2020-04-28 15:32:02 -07:00
Girish Ramakrishnan 73623f2e92 add serviceConfig to appdb
part of #671
2020-04-28 15:31:58 -07:00
Girish Ramakrishnan fbcc4cfa50 Rename KNOWN_ADDONS to ADDONS 2020-04-27 22:59:35 -07:00
Girish Ramakrishnan 474a3548e0 Rename KNOWN_SERVICES to SERVICES 2020-04-27 22:59:11 -07:00
Girish Ramakrishnan 2cdf68379b Revert "add volume support"
This reverts commit b8bb69f730.

Revert this for now, we will try a simpler non-object volume first
2020-04-27 22:55:43 -07:00
Girish Ramakrishnan cc8509f8eb More 5.2 changes 2020-04-26 22:28:43 -07:00
Girish Ramakrishnan a520c1b1cb Update all docker images to use base image 2.0.0 2020-04-26 17:09:31 -07:00
Girish Ramakrishnan 75fc2cbcfb Update base image 2020-04-25 10:37:08 -07:00
Girish Ramakrishnan b8bb69f730 add volume support
part of #668, #569
2020-04-24 22:09:07 -07:00
Girish Ramakrishnan b46d3e74d6 Fix crash in cloudflare error handling 2020-04-23 12:07:54 -07:00
Girish Ramakrishnan 77a1613107 test: fix alias routes 2020-04-22 18:16:33 -07:00
Girish Ramakrishnan 62fab7b09f mail: allow alternate mx 2020-04-22 17:36:34 -07:00
Johannes Zellner 5d87352b28 backupId cannot be null during restore 2020-04-21 16:00:19 +02:00
Girish Ramakrishnan ff60f5a381 move aliases route under mailbox
since aliases can now span domains

fixes #577
2020-04-20 19:17:55 -07:00
Girish Ramakrishnan 7f666d9369 mail: implement aliases across domains
part of #577
2020-04-20 15:19:48 -07:00
Girish Ramakrishnan 442f16dbd0 more changes 2020-04-18 22:56:38 -07:00
Girish Ramakrishnan 2dcab77ed1 Fix issue where app with oauth addon will not backup or uninstall 2020-04-18 10:08:20 -07:00
Girish Ramakrishnan 13be04a169 Deny non-member email immediately 2020-04-18 02:51:31 -07:00
Girish Ramakrishnan e3767c3a54 remove obsolete isadmin flag 2020-04-18 02:32:17 -07:00
Girish Ramakrishnan ce957c8dd5 update mail container 2020-04-18 02:31:59 -07:00
Girish Ramakrishnan 0606b2994c add membersOnly flag to a mailing list 2020-04-17 17:44:14 -07:00
Girish Ramakrishnan 33acccbaaa only check the p key for dkim
this less-strict DKIM check allows users to set a stronger DKIM key
2020-04-17 12:45:21 -07:00
Girish Ramakrishnan 1e097abe86 Add note on dkim key length 2020-04-17 10:29:14 -07:00
Girish Ramakrishnan e51705c41d acme: request ECC certs 2020-04-17 10:22:01 -07:00
Girish Ramakrishnan 7eafa661fe check .well-known presence upstream
this is required for apps like nextcloud which have caldav/cardav
routes
2020-04-15 16:56:41 -07:00
Girish Ramakrishnan 2fe323e587 remove bogus internal route 2020-04-14 23:11:44 -07:00
Girish Ramakrishnan 4e608d04dc 5.1.4 changes 2020-04-11 18:45:39 -07:00
Girish Ramakrishnan 531d314e25 Show error message if gpg failed 2020-04-11 17:11:55 -07:00
Girish Ramakrishnan 1ab23d2902 fix indexOf value comparison 2020-04-11 14:21:05 -07:00
Girish Ramakrishnan b3496e1354 Add ECDHE-RSA-AES128-SHA256 to cipher list
one of our users had the site reverse proxied. it broke after the
5.1 cipher change and they nailed it down to using this cipher.

https://security.stackexchange.com/questions/72926/is-tls-ecdhe-rsa-with-aes-128-cbc-sha256-a-safe-cipher-suite-to-use
says this is safe

The following prints the cipher suite:

    log_format combined2 '$remote_addr - [$time_local] '
        '$ssl_protocol/$ssl_cipher '
        '"$request" $status $body_bytes_sent $request_time '
        '"$http_referer" "$host" "$http_user_agent"';
2020-04-10 09:49:06 -07:00
Girish Ramakrishnan 2efa0aaca4 serve custom well-known documents via nginx 2020-04-09 00:15:56 -07:00
Girish Ramakrishnan ef9aeb0772 Bump default version for tests 2020-04-08 14:24:58 -07:00
Girish Ramakrishnan 924a0136eb 5.1.3 changes 2020-04-08 13:52:53 -07:00
Girish Ramakrishnan c382fc375e Set the resetTokenCreationTime in invitation links 2020-04-08 13:11:24 -07:00
Girish Ramakrishnan 2544acddfa Fix crash with misconfigured reverse proxy
https://forum.cloudron.io/topic/2288/mastodon-terminal-not-starting
2020-04-08 09:43:43 -07:00
Johannes Zellner 58072892d6 Add 5.1.2 changes 2020-04-08 11:52:32 +02:00
Johannes Zellner 85a897c78c Remove console.log debug leftover 2020-04-08 11:48:12 +02:00
Girish Ramakrishnan 6adf5772d8 update turn config to prevent internal access
https://www.rtcsec.com/2020/04/01-slack-webrtc-turn-compromise/
2020-04-07 15:37:31 -07:00
Girish Ramakrishnan f98e3b1960 more 5.1.1 changes 2020-04-03 10:41:37 -07:00
Johannes Zellner 671a967e35 Add 5.1.1 changes 2020-04-03 13:33:03 +02:00
Johannes Zellner 950ef0074f Add libcurl3-gnutls as explicit dependency 2020-04-03 09:45:03 +02:00
Girish Ramakrishnan 5515324fd4 coturn -> turn in docker repo name 2020-04-02 19:51:14 -07:00
Girish Ramakrishnan e72622ed4f Fix crash during auto-update 2020-04-02 19:47:29 -07:00
Girish Ramakrishnan e821733a58 add note on exposed ports 2020-04-02 18:09:26 -07:00
Girish Ramakrishnan a03c0e4475 mail: disable hostname validation 2020-04-02 15:00:11 -07:00
Girish Ramakrishnan 3203821546 typo 2020-04-02 12:29:20 -07:00
Girish Ramakrishnan 16f3cee5c5 install custom nginx only on xenial
https://nginx.org/en/linux_packages.html#Ubuntu
http://nginx.org/packages/ubuntu/pool/nginx/n/nginx/
2020-04-02 11:54:22 -07:00
Johannes Zellner 57afb46cbd Ensure nginx installation will not overwrite our conf files 2020-04-02 16:57:55 +02:00
Johannes Zellner 91dde5147a add-apt-repository does not call apt-get update 2020-04-02 13:54:39 +02:00
Johannes Zellner d0692f7379 Ensure we have latest nginx 2020-04-02 12:37:02 +02:00
Girish Ramakrishnan e360658c6e More changes 2020-04-01 17:00:01 -07:00
Girish Ramakrishnan e7dc77e6de bump mail container for mailbox size fix 2020-04-01 16:31:07 -07:00
Girish Ramakrishnan e240a8b58f add comment on the struct 2020-04-01 16:26:16 -07:00
Girish Ramakrishnan 38d4f2c27b Add note on what df output is 2020-04-01 15:59:48 -07:00
Girish Ramakrishnan 552e2a036c Use block size instead of apparent size in du
https://stackoverflow.com/questions/5694741/why-is-the-output-of-du-often-so-different-from-du-b

df uses superblock info to get consumed blocks/disk size. du with -b
prints actual file size instead of the disk space used by the files.
2020-04-01 15:24:53 -07:00
Johannes Zellner 2d4b978032 It will be 5.1.0 2020-04-01 22:30:50 +02:00
Johannes Zellner 36e00f0c84 We will release a 5.0.7 patch release first 2020-04-01 22:26:23 +02:00
Johannes Zellner ef64b2b945 Use coturn addon tag 1.0.0 2020-04-01 21:50:21 +02:00
Johannes Zellner f6cd33ae24 Set turn secret for apps 2020-04-01 21:50:09 +02:00
Girish Ramakrishnan dd109f149f mail: fix eventlog db perms 2020-04-01 12:24:54 -07:00
Girish Ramakrishnan 5b62d63463 clear mailbox on update and restore
part of #669
2020-03-31 17:51:27 -07:00
Girish Ramakrishnan 3fec599c0c remove mail domain add/remove API
merge this as a transaction into domains API

fixes #669
2020-03-31 14:48:19 -07:00
Girish Ramakrishnan e30ea9f143 make mailbox domain nullable
for apps that do not use sendmail/recvmail addon, these are now null.
otherwise, there is no way to edit the mailbox in the UI

part of #669
2020-03-31 11:26:19 -07:00
Johannes Zellner 7cb0c31c59 Also restart turn server on dashboard domain change 2020-03-31 14:52:09 +02:00
Johannes Zellner b00a7e3cbb Update turn addon 2020-03-31 10:55:41 +02:00
Johannes Zellner e63446ffa2 Support persistent turn secret 2020-03-31 09:28:57 +02:00
Girish Ramakrishnan 580da19bc2 Less strict dmarc validation
fixes #666
2020-03-30 19:32:25 -07:00
Girish Ramakrishnan 936f456cec make reset tokens only valid for a day
fixes #563

mysql timestamps cannot be null. it will become current timestamp when
set as null
2020-03-30 17:13:31 -07:00
Girish Ramakrishnan 5d6a02f73c mysql: create the my.cnf in run time dir 2020-03-30 16:32:54 -07:00
Girish Ramakrishnan b345195ea9 add missing fields in users table 2020-03-30 16:32:28 -07:00
Girish Ramakrishnan 3e6b66751c typoe in assert 2020-03-30 15:17:34 -07:00
Johannes Zellner f78571e46d Support reserved port ranges 2020-03-30 10:01:52 +02:00
Johannes Zellner f52000958c Update manifest format to 5.1.1 2020-03-30 08:43:28 +02:00
Johannes Zellner 5ac9c6ce02 add turn,stun ports to RESERVED ones
We still need to protect the TURN port range
2020-03-30 08:30:06 +02:00
Johannes Zellner 1110a67483 Add turn addon setup and teardown calls 2020-03-30 08:24:52 +02:00
Girish Ramakrishnan 57bb1280f8 better error message 2020-03-29 20:12:59 -07:00
Girish Ramakrishnan 25c000599f Fix assert (appStoreId is optional) 2020-03-29 19:12:07 -07:00
Girish Ramakrishnan 86f45e2769 Fix failing test 2020-03-29 18:55:44 -07:00
Girish Ramakrishnan 7110240e73 Only a Cloudron owner can install/update/exec apps with the docker addon
this should have been part of f1975d8f2b
2020-03-29 18:52:37 -07:00
Girish Ramakrishnan 1da37b66d8 use resource pattern in apps routes
this makes it easy to implement access control in route handlers
2020-03-29 17:11:10 -07:00
Girish Ramakrishnan f1975d8f2b only owner can install/repair/update/exec docker addon apps 2020-03-29 16:24:04 -07:00
Girish Ramakrishnan f407ce734a restrict the app to bind mount under /app/data only
rest have to be volumes
2020-03-29 13:57:45 -07:00
Girish Ramakrishnan f813cfa8db Listen only on the docker interface 2020-03-29 13:11:16 -07:00
Girish Ramakrishnan d5880cb953 TODO block is obsolete 2020-03-29 13:10:19 -07:00
Girish Ramakrishnan 95da9744c1 Prefix env vars with CLOUDRON_ 2020-03-29 09:35:34 -07:00
Girish Ramakrishnan 85c3e45cde remove oauth addon code 2020-03-29 09:35:34 -07:00
Johannes Zellner 520a396ded Use turn server with certificates 2020-03-29 09:32:48 +02:00
Johannes Zellner 13ad611c96 Remove ssh related settings from the turn container config 2020-03-29 09:32:48 +02:00
Girish Ramakrishnan 85f58d9681 more changes 2020-03-28 23:10:17 -07:00
Johannes Zellner c1de62acef Update coturn 2020-03-29 07:30:42 +02:00
Johannes Zellner 7e47e36773 Fix portrange notation in firewall service 2020-03-29 07:25:36 +02:00
Johannes Zellner 00b6217cab Fix turn tls port 2020-03-29 07:09:17 +02:00
Girish Ramakrishnan acc2b5a1a3 remove unused param 2020-03-28 22:05:43 -07:00
Girish Ramakrishnan b06feaa36b more changes 2020-03-28 17:48:55 -07:00
Johannes Zellner 89cf8a455a Allow turn and stun service ports 2020-03-28 23:33:44 +01:00
Johannes Zellner 710046a94f Add coturn addon service 2020-03-28 22:46:32 +01:00
Johannes Zellner b366b0fa6a Stop container with isCloudronManged labels instead of by network 2020-03-28 22:46:32 +01:00
Girish Ramakrishnan f9e7a8207a cloudron-support: make it --owner-login 2020-03-27 18:58:12 -07:00
Johannes Zellner 6178bf3d4b Update sftp addon 2020-03-27 14:54:35 +01:00
Girish Ramakrishnan f3b979f112 More 5.0.6 changelog 2020-03-26 21:56:18 -07:00
Girish Ramakrishnan 9faae96d61 make app password work with sftp 2020-03-26 21:50:25 -07:00
Girish Ramakrishnan 2135fe5dd0 5.0.6 changelog
(cherry picked from commit 3c1a1f1b81)
2020-03-26 19:32:58 -07:00
Girish Ramakrishnan 007a8d248d make eventlog routes owner only 2020-03-26 18:54:16 -07:00
Girish Ramakrishnan 58d4a3455b email: add type filter to eventlog 2020-03-25 22:05:49 -07:00
Girish Ramakrishnan 8e3c14f245 5.0.5 changes
(cherry picked from commit cc6ddf50b1)
2020-03-25 08:13:38 -07:00
Girish Ramakrishnan 91af2495a6 Make key validation work for ecc certs 2020-03-24 21:20:21 -07:00
Girish Ramakrishnan 7d7df5247b Update cipher suite based on ssl-config recommendation
ssl_prefer_server_ciphers off is the recommendation since the cpihers
are deprecated

https://serverfault.com/questions/997614/setting-ssl-prefer-server-ciphers-directive-in-nginx-config
2020-03-24 19:24:58 -07:00
Girish Ramakrishnan f99450d264 Enable TLSv1.3 and remove TLSv1 and 1.1
IE10 does not have 1.2, so maybe we can risk it

As per Android documentaion TLS 1.2 is fully supported after API level 20/Android 5(Lolipop)

https://discussions.qualys.com/thread/17020-tls-12-support-for-android-devices
https://www.ryandesignstudio.com/what-is-tls/
2020-03-24 14:37:08 -07:00
Girish Ramakrishnan d3eeb5f48a mail: disable host and proto mismatch 2020-03-24 11:50:52 -07:00
Girish Ramakrishnan 1e8a02f91a Make token expiry a year
we now have a UI to invalid all tokens easily, so this should be OK.
2020-03-23 21:51:13 -07:00
Girish Ramakrishnan 97c3bd8b8e mail: incoming mail from dynamic hostnames was rejected 2020-03-23 21:50:36 -07:00
Girish Ramakrishnan 09ce27d74b bump default token expiry to a month 2020-03-21 18:46:38 -07:00
Girish Ramakrishnan 2447e91a9f mail: throttle denied events 2020-03-20 14:04:16 -07:00
Girish Ramakrishnan e6d881b75d Use owner email for LE certs
https://forum.cloudron.io/topic/2244/email-contact-on-let-s-encrypt-ssl-tls-certificates-uses-password-recovery-email-rather-than-primary-email-address
2020-03-20 13:39:58 -07:00
Girish Ramakrishnan 36f963dce8 remove unncessary debug in routes 2020-03-19 17:05:31 -07:00
Girish Ramakrishnan 1b15d28212 eventlog: add start/stop/restart logs 2020-03-19 17:02:55 -07:00
Girish Ramakrishnan 4e0c15e102 use short form syntax 2020-03-19 16:48:31 -07:00
Girish Ramakrishnan c9e40f59de bump the timeout for really slow disks 2020-03-19 13:33:53 -07:00
Girish Ramakrishnan 38cf31885c Make backup configure owner only 2020-03-18 17:23:23 -07:00
Girish Ramakrishnan 4420470242 comcast does not allow port 25 check anymore 2020-03-17 13:55:35 -07:00
Girish Ramakrishnan 9b05786615 appstore: add whitelist/blacklist 2020-03-15 17:20:48 -07:00
Girish Ramakrishnan 725b2c81ee custom.yml is obsolete 2020-03-15 16:50:42 -07:00
Girish Ramakrishnan 661965f2e0 Add branding tests 2020-03-15 16:38:15 -07:00
Girish Ramakrishnan 7e0ef60305 Fix incorrect role comparison 2020-03-15 16:19:22 -07:00
Girish Ramakrishnan 2ac0fe21c6 ghost file depends on base dir 2020-03-15 11:41:39 -07:00
Girish Ramakrishnan b997f2329d make branding route for owner only 2020-03-15 11:39:02 -07:00
Girish Ramakrishnan 23ee758ac9 do not check for updates for stopped apps 2020-03-15 09:48:08 -07:00
Girish Ramakrishnan 9ea12e71f0 linode: dns backend
the dns is very slow - https://github.com/certbot/certbot/pull/6320
takes a good 15 minutes at minimum to propagate

https://certbot-dns-linode.readthedocs.io/en/stable/
https://www.linode.com/community/questions/17296/linode-dns-propagation-time
2020-03-13 11:44:43 -07:00
Girish Ramakrishnan d3594c2dd6 change ownership of ghost file for good measure 2020-03-12 10:30:51 -07:00
Girish Ramakrishnan 6ee4b0da27 Move out ghost file to platformdata
Since /tmp is world writable this might cause privilege escalation

https://forum.cloudron.io/topic/2222/impersonate-user-privilege-escalation
2020-03-12 10:24:21 -07:00
Girish Ramakrishnan 3e66feb514 mail: add mailbox acl 2020-03-10 22:12:15 -07:00
Girish Ramakrishnan cd91a5ef64 5.0.3 changes 2020-03-10 17:18:21 -07:00
Girish Ramakrishnan cf89609633 mail: acl was enabled by mistake 2020-03-10 17:15:23 -07:00
Girish Ramakrishnan 67c24c1282 mail: make spamd_user case insensitive 2020-03-10 12:08:43 -07:00
Girish Ramakrishnan 7d3df3c55f Fix sa usage 2020-03-10 09:22:41 -07:00
Girish Ramakrishnan dfe5cec46f Show the public IP to finish setup 2020-03-09 15:18:39 -07:00
Girish Ramakrishnan 17c881da47 Fix spam training 2020-03-09 13:51:17 -07:00
Girish Ramakrishnan 6e30c4917c Do not wait for dns when re-configured 2020-03-09 12:36:29 -07:00
Girish Ramakrishnan c6d4f0d2f0 mail: fix word boundary regexp 2020-03-07 19:16:10 -08:00
Girish Ramakrishnan b32128bebf Fix quoting in emails 2020-03-07 19:12:39 -08:00
Girish Ramakrishnan a3f3d86908 More spam fixes 2020-03-07 18:52:20 -08:00
Girish Ramakrishnan b29c82087a Bump the mail container version 2020-03-07 17:08:35 -08:00
Johannes Zellner 657beda7c9 Copy 5.0.0 changes for 5.0.1 2020-03-07 16:56:40 -08:00
Girish Ramakrishnan b4f5ecb304 mail: fix eventlog search 2020-03-07 15:56:56 -08:00
Girish Ramakrishnan 3dabad5e91 Detect that domain is in use by app correctly 2020-03-07 14:52:34 -08:00
Johannes Zellner 890b46836b Do not allow lower level roles to edit higher level ones 2020-03-07 13:53:01 -08:00
Girish Ramakrishnan 835b3224c6 disable getting user token in demo mode 2020-03-07 11:44:38 -08:00
Girish Ramakrishnan f8d27f3139 mail: Fix ownership issue with /app/data 2020-03-07 11:40:49 -08:00
Girish Ramakrishnan 33f263ebb9 Fix spamd logs 2020-03-07 01:00:08 -08:00
Girish Ramakrishnan 027925c0ba Only do spam processing when we have incoming domains 2020-03-07 00:22:00 -08:00
Girish Ramakrishnan 17c4819d41 eventlog updates 2020-03-06 23:16:32 -08:00
Johannes Zellner 017d19a8c8 Do not send internal link for update notification 2020-03-06 19:18:01 -08:00
Girish Ramakrishnan 46b6e319f5 add some spacing in the footer 2020-03-06 19:13:37 -08:00
Johannes Zellner 8f087e1c30 Take default footer from constants and keep settingsdb pristine 2020-03-06 18:08:26 -08:00
Johannes Zellner c3fc0e83a8 Optimize collectd restart to be skipped if profile hasn't actually changed 2020-03-06 17:44:31 -08:00
Johannes Zellner 7ed0ef7b37 Ensure collectd backup config on startup 2020-03-06 17:44:31 -08:00
Girish Ramakrishnan 46ede3d60d search for request_uri in try_files
this lets us put images in app_not_responding.html
2020-03-06 17:01:48 -08:00
Girish Ramakrishnan 7a63fd4711 Failed quickly if docker image not found 2020-03-06 16:39:20 -08:00
Girish Ramakrishnan 65f573b773 mail container update 2020-03-06 16:11:52 -08:00
Johannes Zellner afa2fe8177 Improve role add/edit error message 2020-03-06 13:16:50 -08:00
Girish Ramakrishnan ad72a8a929 Add comment 2020-03-06 13:05:31 -08:00
Johannes Zellner a7b00bad63 Fixup status code typo 2020-03-06 11:59:31 -08:00
Johannes Zellner 85fd74135c Bring back legacy ldap mailbox search for old sogo 2020-03-06 11:48:51 -08:00
Girish Ramakrishnan 970ccf1ddb send footer in status route
required for login screen to be customized
2020-03-06 01:16:48 -08:00
Johannes Zellner b237eb03f6 Add support feature flag 2020-03-06 01:08:45 -08:00
Girish Ramakrishnan a569294f86 Better changelog 2020-03-06 01:03:52 -08:00
Johannes Zellner 16f85a23d2 Clear reboot notification if reboot is triggered 2020-03-06 00:49:00 -08:00
Johannes Zellner fcee8aa5f3 Improve LDAP mailbox searches to better suit sogo 2020-03-06 00:48:41 -08:00
Johannes Zellner d85eabce02 Update reboot required notification text 2020-03-05 21:01:15 -08:00
Johannes Zellner de23d1aa03 Do not allow to set active flag for the operating user 2020-03-05 21:00:59 -08:00
Johannes Zellner 1766bc6ee3 For now we enable all features 2020-03-05 13:37:07 -08:00
Girish Ramakrishnan c1801d6e71 Add linode-oneclick provider 2020-03-05 11:25:43 -08:00
Girish Ramakrishnan 64844045ca mail: various pam related fixes 2020-03-04 15:00:37 -08:00
Girish Ramakrishnan e90da46967 spam: add default corpus and global db 2020-03-02 21:45:48 -08:00
Girish Ramakrishnan d10957d6df remove galaxygate from cloudron-setup help 2020-02-28 11:14:06 -08:00
Girish Ramakrishnan 50dc90d7ae remove galaxygate 2020-02-28 11:13:44 -08:00
Johannes Zellner 663bedfe39 Sync default features 2020-02-28 15:18:16 +01:00
Girish Ramakrishnan ce9834757e restore: carefully replace backup config
do not replace the backup policy and other flags
2020-02-27 12:38:37 -08:00
Girish Ramakrishnan cc932328ff fix comment 2020-02-27 10:36:35 -08:00
Girish Ramakrishnan 4ebe143a98 improve the error message on domain removal 2020-02-27 10:12:39 -08:00
Johannes Zellner 82aff74fc2 Make app passwords stronger 2020-02-27 13:07:01 +01:00
Girish Ramakrishnan 6adc099455 Fix crash 2020-02-26 15:49:41 -08:00
Girish Ramakrishnan 35efc8c650 add linode objectstorage backend 2020-02-26 09:08:30 -08:00
Girish Ramakrishnan 3f63d79905 Fixup version of next release 2020-02-26 09:01:48 -08:00
Girish Ramakrishnan 00096f4dcd fix comment 2020-02-26 09:01:35 -08:00
Girish Ramakrishnan c3e0d9086e cloudron-support: backups and appsdata can be empty 2020-02-24 14:12:25 -08:00
Girish Ramakrishnan f1dfe3c7e8 mail: Fix crash when determining usage 2020-02-24 11:45:17 -08:00
Johannes Zellner 6f96ff790f Groups are part of user manager role 2020-02-24 17:49:22 +01:00
Johannes Zellner ccb218f243 setPassword wants the full user object 2020-02-24 13:21:17 +01:00
Girish Ramakrishnan 9ac194bbea fix missing quote in debug message 2020-02-23 11:15:30 -08:00
Girish Ramakrishnan 0191907ce2 mail: use limit plugin instead of rcpt_to.max_count 2020-02-23 11:15:30 -08:00
Johannes Zellner e80069625b Fix typo in migration script 2020-02-22 15:26:16 +01:00
Girish Ramakrishnan 0e156b9376 migrate permissions and admin flag to user.role 2020-02-21 16:49:20 -08:00
Johannes Zellner a8f1b0241e Add route to obtain an appstore accessToken 2020-02-21 12:34:54 +01:00
Girish Ramakrishnan 6715cf23d7 Add mail usage info 2020-02-20 12:09:06 -08:00
Girish Ramakrishnan 82a173f7d8 proxy requests to mail server 2020-02-20 10:10:34 -08:00
Johannes Zellner 857504c409 Add function to retrieve appstore user access token 2020-02-20 17:05:07 +01:00
Johannes Zellner 4b4586c1e5 Get features from the appstore 2020-02-20 16:04:22 +01:00
Girish Ramakrishnan 6679fe47df mail: add X-Envelope-From/To headers 2020-02-19 22:14:23 -08:00
Girish Ramakrishnan e7a98025a2 disable update of domain in demo mode
we removed the locked flag, so we have to add this check
2020-02-19 10:45:55 -08:00
Girish Ramakrishnan 2870f24bec mail eventlog: add remote info 2020-02-18 21:31:28 -08:00
Girish Ramakrishnan 037440034b Move collectd logs to platformdata and rotate it 2020-02-18 20:36:50 -08:00
Johannes Zellner 15cc1f92e3 Fix typo 2020-02-17 13:47:21 +01:00
Girish Ramakrishnan 00c6ad675e add usermanager tests 2020-02-14 14:34:29 -08:00
Girish Ramakrishnan 655a740b0c split tests into various sections 2020-02-14 14:04:51 -08:00
Girish Ramakrishnan 028852740d Make users-test work 2020-02-14 13:23:17 -08:00
Johannes Zellner c8000fdf90 Fix the features selection 2020-02-14 15:21:56 +01:00
Johannes Zellner 995e56d7e4 Also grant education and contributor subscriptions all features 2020-02-14 15:13:21 +01:00
Johannes Zellner c20d3b62b0 Determin features based on subscription and cloudron creation 2020-02-14 15:07:25 +01:00
Girish Ramakrishnan c537dfabb2 add manage user permission 2020-02-13 22:49:58 -08:00
Girish Ramakrishnan 11b5304cb9 userdb: only pass specific fields to update 2020-02-13 22:45:14 -08:00
Girish Ramakrishnan fd8abbe2ab remove ROLE_USER
every authenticated user has ROLE_USER. So, this role is superfluous
2020-02-13 21:53:57 -08:00
Girish Ramakrishnan 25d871860d domains: remove locked field
we will do this as part of access control if needed later
2020-02-13 21:16:46 -08:00
Girish Ramakrishnan d1911be28c user: load the resource with middleware 2020-02-13 20:59:17 -08:00
Girish Ramakrishnan 938ca6402c mail: add search param 2020-02-13 09:08:47 -08:00
Johannes Zellner 0aaecf6e46 Cannot use Infinity 2020-02-13 17:09:28 +01:00
Johannes Zellner b06d84984b Add features to config object 2020-02-13 16:34:29 +01:00
Girish Ramakrishnan 51b50688e4 mail eventlog: fix bounce event 2020-02-12 23:33:43 -08:00
Girish Ramakrishnan 066d7ab972 Update mail container 2020-02-12 22:11:11 -08:00
Girish Ramakrishnan e092074d77 2020 is unused 2020-02-11 22:12:34 -08:00
Girish Ramakrishnan 83bdcb8cc4 remove unused domain stats route 2020-02-11 22:10:57 -08:00
Girish Ramakrishnan f80f40cbcd repair: take optional docker image for re-configure 2020-02-11 21:05:01 -08:00
Girish Ramakrishnan 4b93b31c3d SCOPE_* vars are unused now 2020-02-11 17:37:12 -08:00
Girish Ramakrishnan 4d050725b7 storage: done events must be called next tick
It seems that listDir() returns synchronously (!), not sure how.
This results in the done event getting called with an error but
the EE event handlers are not setup yet.
2020-02-11 11:48:49 -08:00
Girish Ramakrishnan 57597bd103 s3: bucket name cannot contain / 2020-02-11 11:19:47 -08:00
Girish Ramakrishnan fb52c2b684 backupupload: it is either result or message 2020-02-11 10:03:26 -08:00
Girish Ramakrishnan de547df9bd Show docker image in the error 2020-02-10 21:54:08 -08:00
Girish Ramakrishnan a05342eaa0 Add mail eventlog 2020-02-10 15:36:30 -08:00
Girish Ramakrishnan fb931b7a3a More 4.5 changes 2020-02-10 14:32:15 -08:00
Girish Ramakrishnan d1c07b6d30 cron: rework recreation of jobs based on timezone 2020-02-10 13:12:20 -08:00
Johannes Zellner 7f0ad2afa0 Move login tests to cloudron route tests 2020-02-10 16:40:07 +01:00
Johannes Zellner d8e0639db4 Empty or missing username/password results in 400 2020-02-10 16:14:22 +01:00
Johannes Zellner 4d91351845 Get config should succeed for non-admins also 2020-02-10 13:10:56 +01:00
Johannes Zellner d3f08ef580 Fix apps test to use latest test-app 2020-02-08 00:43:57 +01:00
Johannes Zellner 5e11a9c8ed Fixup typo 2020-02-07 23:12:53 +01:00
Johannes Zellner 957e1a7708 Cleanup unused tokendb apis 2020-02-07 23:06:45 +01:00
Johannes Zellner 7c86ed9783 Add ability to specify the login purpose for further use
In this case the cli will specify a different token type
2020-02-07 23:03:53 +01:00
Girish Ramakrishnan 799b588693 More 4.5 changes 2020-02-07 11:29:16 -08:00
Girish Ramakrishnan 596f4c01a4 cloudron-setup: remove support for pre-4.2 2020-02-07 09:15:12 -08:00
Girish Ramakrishnan f155de0f17 Revert "Read the provider from the settings, not the migration PROVIDER_FILE"
This reverts commit 001749564d.

PROVIDER is still very much alive and active. sysinfo provider is for the network
interface
2020-02-07 09:13:33 -08:00
Johannes Zellner 476ba1ad69 Fix token expiresAt 2020-02-07 16:42:15 +01:00
Johannes Zellner ac4aa4bd3d Add tokens routes 2020-02-07 16:20:05 +01:00
Girish Ramakrishnan 237f2c5112 Better error message for domain conflict 2020-02-06 15:51:32 -08:00
Johannes Zellner cbc6785eb5 Fix typo 2020-02-06 17:29:45 +01:00
Johannes Zellner 26c4cdbf17 Rename tokens.addTokenByUserId() to simply tokens.add() 2020-02-06 17:26:17 +01:00
Johannes Zellner fb78f31891 cleanup accesscontrol route tests for now 2020-02-06 17:26:17 +01:00
Johannes Zellner 2b6bf8d195 Remove Oauth clients code 2020-02-06 17:26:15 +01:00
Johannes Zellner 2854462e0e Remove token scope business 2020-02-06 16:44:46 +01:00
Johannes Zellner b4e4b11ab3 Remove now redundant developer login code 2020-02-06 15:47:44 +01:00
Johannes Zellner 7c5a258af3 Move 2fa validation in one place 2020-02-06 15:36:14 +01:00
Johannes Zellner 12aa8ac0ad Remove passport 2020-02-06 14:56:28 +01:00
Johannes Zellner 58d8f688e5 Update schema since authcodes is gone 2020-02-06 11:11:15 +01:00
Girish Ramakrishnan 7efb9e817e oauth2 is gone 2020-02-05 14:46:09 -08:00
Girish Ramakrishnan 5145ea3530 Add supportConfig in database 2020-02-05 14:46:05 -08:00
Girish Ramakrishnan 2f6933102c put appstore whitelist/blacklist in db 2020-02-05 11:58:10 -08:00
Girish Ramakrishnan 25ef5ab636 Move custom pages to a subdirectory 2020-02-05 11:42:17 -08:00
Johannes Zellner 4ae12ac10b Remove oauth
A whole bunch of useless stuff
2020-02-05 18:15:59 +01:00
Johannes Zellner bfffde5f89 Remove oauth based account setup page 2020-02-05 17:10:55 +01:00
Johannes Zellner aa7ec53257 Also send display name with invite link 2020-02-05 16:34:34 +01:00
Johannes Zellner 1f41e6dc0f Fix audit source usage 2020-02-05 16:12:40 +01:00
Johannes Zellner 1fbbaa82ab Generate the user invite link only in one location 2020-02-05 15:53:05 +01:00
Johannes Zellner 68b1d1dde1 Fixup account setup link 2020-02-05 15:21:55 +01:00
Johannes Zellner d773cb4873 Add REST route for account setup
This replaces the server side rendered form
2020-02-05 15:04:59 +01:00
Johannes Zellner d3c7616120 Remove csurf
New views will be using the REST api not session, so this won't apply
2020-02-05 12:49:37 +01:00
Johannes Zellner 6a92af3db3 Remove password reset views from oauth 2020-02-05 11:43:33 +01:00
Girish Ramakrishnan 763e14f55d Make app error page customizable 2020-02-04 17:52:30 -08:00
Girish Ramakrishnan 4f57d97fff add api to get/set footer and remove all use of custom.js 2020-02-04 13:27:19 -08:00
Girish Ramakrishnan 3153fb5cbe custom: remove alerts section 2020-02-04 13:09:00 -08:00
Girish Ramakrishnan c9e96cd97a custom: remove support section 2020-02-04 13:07:36 -08:00
Girish Ramakrishnan c41042635f custom: remove subscription.configurable 2020-02-04 12:58:32 -08:00
Girish Ramakrishnan 141b2d2691 custom: remove app whitelist/blacklist 2020-02-04 12:58:11 -08:00
Girish Ramakrishnan e71e8043cf custom: remove config.uiSpec.domains 2020-02-04 12:56:10 -08:00
Girish Ramakrishnan 49d80dbfc4 custom: remove backups.configurable 2020-02-04 12:49:41 -08:00
Johannes Zellner 8d6eca2349 Fix typos 2020-02-04 18:32:43 +01:00
Johannes Zellner 13d0491759 Send out new password reset link 2020-02-04 17:11:31 +01:00
Johannes Zellner 37e2d78d6a Users without a username have to sign up first 2020-02-04 17:07:45 +01:00
Johannes Zellner 6745221e0f Password reset does not need an email 2020-02-04 17:05:08 +01:00
Johannes Zellner 18bbe70364 Add route to set new password 2020-02-04 16:47:57 +01:00
Johannes Zellner eec8d4bdac We want to redirect to login.html 2020-02-04 15:59:12 +01:00
Johannes Zellner 86029c1068 Add new password reset route 2020-02-04 15:27:22 +01:00
Johannes Zellner 0ae9be4de9 Add basic login/logout logic 2020-02-04 14:35:25 +01:00
Girish Ramakrishnan 57e3180737 typo 2020-02-01 18:12:33 -08:00
Girish Ramakrishnan a84cdc3d09 app password: add tests for the rest routes 2020-02-01 10:19:14 -08:00
Girish Ramakrishnan a5f35f39fe oom notification for backup disk as well 2020-01-31 22:20:34 -08:00
Girish Ramakrishnan 3427db3983 Add app passwords feature 2020-01-31 22:03:19 -08:00
Girish Ramakrishnan e3878fa381 mysqldump: Add --column-statistics=0
mysqldump: Couldn't execute 'SELECT COLUMN_NAME,                       JSON_EXTRACT(HISTOGRAM, '$."number-of-buckets-specified"')                FROM information_schema.COLUMN_STATISTICS                WHERE SCHEMA_NAME = 'box' AND TABLE_NAME = 'appAddonConfigs';': Unknown table 'COLUMN_STATISTICS' in information_schema (1109)
2020-01-31 18:42:44 -08:00
Girish Ramakrishnan e1ded9f7b5 Add collectd for backups 2020-01-31 14:56:41 -08:00
Girish Ramakrishnan 1981493398 refactor code into collectd.js 2020-01-31 13:33:19 -08:00
Girish Ramakrishnan dece7319cc Update packages carefully 2020-01-31 10:25:47 -08:00
Girish Ramakrishnan 5c4e163709 revert package changes 2020-01-31 10:04:49 -08:00
Johannes Zellner d1acc6c466 Do not upgrade async module since api has changed
We have to first fix for example doWhilst() usage
2020-01-31 15:44:41 +01:00
Girish Ramakrishnan f879d6f529 Prepare for 4.4.5 2020-01-30 21:11:20 -08:00
Girish Ramakrishnan 1ac38d4921 After node update, we get a buffer 2020-01-30 16:06:11 -08:00
Johannes Zellner 4818e9a8e4 Pass cloudron purpose to appstore 2020-01-30 16:00:38 +01:00
Girish Ramakrishnan c4ed471d1c Update node to 10.18.1 2020-01-29 20:54:57 -08:00
Girish Ramakrishnan 83c0b2986a Update mysql packet size 2020-01-29 20:44:26 -08:00
Girish Ramakrishnan b8cddf559a min cpu shares is 2 2020-01-28 22:38:54 -08:00
Girish Ramakrishnan 4ba9f80d44 apps: configure cpuShares 2020-01-28 22:16:25 -08:00
Girish Ramakrishnan d1d3309e91 Better error message for invalid data directories 2020-01-28 14:12:56 -08:00
Girish Ramakrishnan 84cffe8888 Fix debug 2020-01-28 13:51:03 -08:00
Girish Ramakrishnan 3929b3ca0a service: add memorySwap to configure route 2020-01-28 13:33:43 -08:00
Girish Ramakrishnan d649a470f9 More changes 2020-01-28 09:37:48 -08:00
Girish Ramakrishnan db330b23cb Stopped apps should not renew certificates
We had a case where a stopped/ununsed app was generating cert renewal
errors.

One idea might be to suppress the notification as well.
2020-01-26 16:22:20 -08:00
Girish Ramakrishnan cda649884e eventlog: add mailbox and list update events 2020-01-24 17:18:34 -08:00
Girish Ramakrishnan 45053205db refactor: re-order arguments 2020-01-24 17:18:34 -08:00
Johannes Zellner 3f1533896e Keep debug messages in sync 2020-01-21 16:14:36 +01:00
Girish Ramakrishnan e20dfe1b26 Ensure backup is the night of the timezone 2020-01-20 17:28:53 -08:00
Johannes Zellner 946d9db296 We have 2020 also in the oauth login views 2020-01-20 17:47:26 +01:00
Girish Ramakrishnan 6dc2e1aa14 Do not show error page for 503
WP maintenance mode plugin will return 503
2020-01-13 15:00:18 -08:00
Johannes Zellner 001749564d Read the provider from the settings, not the migration PROVIDER_FILE 2020-01-13 15:35:44 +01:00
Johannes Zellner ffcba4646c Add 4.4.5 changes 2020-01-09 16:24:26 +01:00
Girish Ramakrishnan 01d0c8eb9c Remove tz detection
we now have a UI to set this by hand
2020-01-08 09:24:23 -08:00
Girish Ramakrishnan 0cf40bd207 More 4.4.4 changes 2020-01-07 18:31:10 -08:00
Girish Ramakrishnan 4a283e9f35 4.4.4 changes 2020-01-06 08:55:22 -08:00
Johannes Zellner 5ab37bcf7e Disable test if dns setup succeeds twice 2020-01-06 12:21:36 +01:00
Johannes Zellner 9151965cd6 Keep user objects in REST api responses more coherent 2020-01-06 11:54:00 +01:00
Girish Ramakrishnan c5cd71f9e3 Disable motd-news
https://forum.cloudron.io/topic/2050/switch-to-debian-ubuntu-spying
2020-01-05 15:25:15 -08:00
Girish Ramakrishnan 602b335c0e add openldap compat
apps like firefly-iii seem to require these fields when using the
openldap driver
2020-01-05 15:14:46 -08:00
Girish Ramakrishnan 837c8b85c2 2020: happy new year 2020-01-02 16:55:47 -08:00
Girish Ramakrishnan 7d16396e72 clone: custom mailbox name is not cloned 2020-01-01 23:05:34 -08:00
Girish Ramakrishnan 66d3d07148 append error message when verifying dns config 2020-01-01 16:17:16 -08:00
Girish Ramakrishnan b5c1161caa add tokenType to cloudflare config 2020-01-01 16:01:39 -08:00
Girish Ramakrishnan b0420889ad cloudflare: add api token support 2019-12-31 16:47:47 -08:00
Girish Ramakrishnan 527819d886 cloudflare: refactor superagent logic 2019-12-31 16:25:49 -08:00
Girish Ramakrishnan 1ad0cff28e Use app.fqdn in output 2019-12-24 11:07:53 -08:00
Johannes Zellner 783ec03ac9 The setup views require webServerOrigin for documentation purpose 2019-12-23 17:15:45 +01:00
Girish Ramakrishnan 6cd395d494 Allow restore from error state 2019-12-20 17:58:42 -08:00
Girish Ramakrishnan 681079e01c repair: reconfigure for all other states
the idea was that the failed routes can be called again in other cases
2019-12-20 17:00:53 -08:00
Girish Ramakrishnan aabbc43769 4.4.3 changes 2019-12-20 11:29:02 -08:00
Girish Ramakrishnan 2692f6ef4e Add restart route for atomicity 2019-12-20 11:15:36 -08:00
Girish Ramakrishnan 887cbb0b22 make percent non-zero 2019-12-18 09:33:44 -08:00
Johannes Zellner ca4fdc1be8 Add azure-image provider argument 2019-12-17 16:42:25 +01:00
Girish Ramakrishnan 93199c7f5b eventlog: support ticket and ssh 2019-12-16 14:06:55 -08:00
Girish Ramakrishnan 4c6566f42f stopped apps should not be updated or auto-updated 2019-12-16 13:29:15 -08:00
Johannes Zellner c38f7d7f93 Make properties explicitly available 2019-12-16 15:21:26 +01:00
Girish Ramakrishnan da85cea329 avatar: remove query param
let the ui add the size and default
2019-12-13 13:45:02 -08:00
Girish Ramakrishnan d5c70a2b11 Add sshd port warning 2019-12-13 11:32:36 -08:00
Girish Ramakrishnan fe355b4bac 4.4.2 changes 2019-12-12 20:44:54 -08:00
Girish Ramakrishnan a7dee6be51 cloudron.runSystemChecks should take a callback 2019-12-12 20:41:03 -08:00
Girish Ramakrishnan 2817dc0603 Not required to run any cron job immediately 2019-12-12 20:39:40 -08:00
Girish Ramakrishnan 6f36c72e88 Fix crash in mail.checkConfiguration 2019-12-12 20:36:27 -08:00
Girish Ramakrishnan 45e806c455 typo in comment 2019-12-12 19:54:59 -08:00
Johannes Zellner bbdd76dd37 Fix and add memory route tests 2019-12-12 13:21:24 +01:00
Johannes Zellner 09921e86c0 Remove redunandant memory property from config
we have a specific route for this now
2019-12-12 12:14:08 +01:00
Girish Ramakrishnan d6e4b64103 4.4.1 changes 2019-12-11 15:27:47 -08:00
Girish Ramakrishnan 9dd3e4537a return 422 on instance id mismatch
the ui redirects otherwise
2019-12-11 15:13:38 -08:00
Girish Ramakrishnan a5f31e8724 Revert "rename ami to aws-mp"
This reverts commit 72ac00b69a.

Existing code relies on this, so don't change it
2019-12-11 12:56:30 -08:00
Girish Ramakrishnan 72ac00b69a rename ami to aws-mp 2019-12-11 12:27:55 -08:00
Girish Ramakrishnan ae5722a7d4 eventlog: typo when mail list is removed 2019-12-11 10:05:45 -08:00
Johannes Zellner 4e3192d450 Avoid double dns setup tracking 2019-12-11 14:02:40 +01:00
Johannes Zellner ccca3aca04 Send setup state to get the actually correct ip 2019-12-10 18:01:07 +01:00
Girish Ramakrishnan e4dd5d6434 Fix crash when uploading file 2019-12-09 15:02:51 -08:00
Girish Ramakrishnan 9a77fb6306 acme2: implement post-as-get
https://tools.ietf.org/html/rfc8555#section-6.3
https://community.letsencrypt.org/t/post-as-get-and-empty-payload-instead-of/86720/3
https://community.letsencrypt.org/t/problem-with-renew-certificates-the-request-message-was-malformed-method-not-allowed/107889/17
2019-12-08 19:17:52 -08:00
Girish Ramakrishnan 3ec5c713bf debug: certFilePath is undefined 2019-12-08 18:23:12 -08:00
Girish Ramakrishnan 837fc27e94 canAutoupdateApp now returns bool 2019-12-08 16:55:56 -08:00
Girish Ramakrishnan 9ad6025310 search and replace gone wrong 2019-12-06 13:52:43 -08:00
Girish Ramakrishnan d765e4c619 add a note 2019-12-06 12:39:46 -08:00
Girish Ramakrishnan f5217236d6 Change the version number 2019-12-06 12:28:08 -08:00
Girish Ramakrishnan 8f8d099faf Add to changes 2019-12-06 12:23:49 -08:00
Girish Ramakrishnan 16660e083f Also set overwriteDns when manifest is not provided 2019-12-06 12:21:28 -08:00
Girish Ramakrishnan 4e35020a1c Set overwriteDns for install task 2019-12-06 12:11:34 -08:00
Girish Ramakrishnan 111e0bcb5f Fix repair route path 2019-12-06 11:44:41 -08:00
Girish Ramakrishnan d7f9a547fc Disable requiredState check for now
there is a race but this is mitigated by the checkAppState non-db logic
for now
2019-12-06 11:29:35 -08:00
Girish Ramakrishnan 6a64f24e98 Fix repair
If a task fails, we can either:
* allow other task ops to be called - we cannot do this because the ops are fine-grained. for example,
  a restore failure removes many things and calling set-memory or set-location in that state won't
  make sense.

* provide a generic repair route - this allows one to override args and call the failed task
  again. this is what we have now but has the issue that this repair function has to know about all
  the other op functions. for example, for argument validation. we can do some complicated refactoring
  to make it work if we want.

* just a generic total re-configure - this does not work because clone/restore/backup/datadir/uninstall/update
  failure leaves the app in a state which re-configure cannot do anything about.

* allow the failed op to be called again - this seems the easiest. we just allow the route to be called again
  in the error state.

* if we hit a state where even providing extra args, cannot get you out of this "error" state, we have to provide
  some repair route. for example, maybe the container disappeared by some docke error. user clicks 'repair' to
  recreate the container. this route does not have to take any args.

The final solution is:
* a failed task can be called again via the route. so we can resubmit any args and we get validation
* repair route just re-configures and can be called in any state to just rebuild container. re-configure is also
  doing only local changes (docker, nginx)
* install/clone failures are fixed using repair route. updated manifest can be passed in.
* UI shows backup selector for restore failures
* UI shows domain selector for change location failulre
2019-12-06 09:56:09 -08:00
Girish Ramakrishnan 37d7be93b5 Move oldManifest out of restoreConfig 2019-12-06 09:56:03 -08:00
Girish Ramakrishnan 9c809aa6e1 remove dead comment 2019-12-06 09:35:08 -08:00
Girish Ramakrishnan 7ab9f3fa2f re-configure does not require oldConfig
this is only needed when changing location now. the configure()
is now entirely local i.e rebuild local container and the reverse
proxy config
2019-12-06 09:23:58 -08:00
Girish Ramakrishnan ffeb484a10 No need to return args as part of task.get
This reverts commit 831e22b4ff.
This reverts commit 6774514bd2.
2019-12-06 08:42:49 -08:00
Girish Ramakrishnan 2ffb32ae60 Skip moving data if source and target are same 2019-12-06 08:09:43 -08:00
Girish Ramakrishnan 905bb92bad s3: ensure BoxError return 2019-12-05 21:50:44 -08:00
Girish Ramakrishnan 3926efd153 restore: only take non-empty backupId 2019-12-05 21:16:35 -08:00
Girish Ramakrishnan c5e5bb90e3 better error message 2019-12-05 21:16:35 -08:00
Girish Ramakrishnan cea543cba5 On backup error, only set the task error
at some point, the backup ui can show this error
2019-12-05 16:34:40 -08:00
Girish Ramakrishnan a8b489624d fix error messages 2019-12-05 16:27:00 -08:00
Girish Ramakrishnan 49d3bddb62 Show download progress when restoring rsync backups 2019-12-05 15:44:52 -08:00
Girish Ramakrishnan c0ff3cbd22 move progressTag to the end 2019-12-05 15:44:52 -08:00
Girish Ramakrishnan 1de97d6967 do not clear localstorage during in-place import 2019-12-05 12:42:08 -08:00
Girish Ramakrishnan a44a82083e Add backups.testProviderConfig
fields like format/retention won't be validated here since it's only
testing the access credentials
2019-12-05 11:55:53 -08:00
Girish Ramakrishnan d57681ff21 put fqdn in the end 2019-12-05 11:15:21 -08:00
Girish Ramakrishnan e3de2f81d3 setup and clear addons before import 2019-12-05 11:12:40 -08:00
Girish Ramakrishnan e8c5f8164c do not delete data dir for in-place import 2019-12-05 11:01:27 -08:00
Girish Ramakrishnan c07e215148 Use BoxError in on error cases 2019-12-05 09:54:29 -08:00
Girish Ramakrishnan 4bb676fb5c add asserts 2019-12-05 09:32:45 -08:00
Johannes Zellner dbdf86edfc No need to return the same data which the route got passed in 2019-12-05 18:02:57 +01:00
Johannes Zellner 2c8e6330ce Do not allow to change the sysinfo in demo mode 2019-12-05 16:06:21 +01:00
Girish Ramakrishnan 1b563854a7 implement in-place import and custom backup config 2019-12-04 19:27:05 -08:00
Girish Ramakrishnan 80b890101b Add changes 2019-12-04 17:53:02 -08:00
Girish Ramakrishnan c3696469ff Add app fqdn to backup progress message 2019-12-04 17:49:31 -08:00
Girish Ramakrishnan 3e08e7c653 Typo in docker socket path 2019-12-04 14:37:00 -08:00
Girish Ramakrishnan 53e39f571c Make addons code remove a BoxError 2019-12-04 14:28:42 -08:00
Girish Ramakrishnan c992853cca lint 2019-12-04 11:18:39 -08:00
Girish Ramakrishnan 85e17b570b Use whilst instead of forever
this gets rid of the Error object
2019-12-04 11:17:44 -08:00
Girish Ramakrishnan 30eccfb54b Use BoxError instead of Error in all places
This moves everything other than the addon code and some 'done' logic
2019-12-04 11:02:54 -08:00
Girish Ramakrishnan 3623831390 Typo 2019-12-04 10:23:16 -08:00
Girish Ramakrishnan d0a3d00492 Use NOT_IMPLEMENTED error code 2019-12-04 10:22:22 -08:00
Girish Ramakrishnan 0b6fbfd910 Better addon error messages 2019-12-04 10:09:57 -08:00
Girish Ramakrishnan 8cfb27fdcd Add changes 2019-12-03 15:39:29 -08:00
Girish Ramakrishnan 841ab54565 better logs 2019-12-03 15:11:27 -08:00
Girish Ramakrishnan a2e9254343 lint 2019-12-03 15:10:06 -08:00
Johannes Zellner 43cb03a292 Send provider and version during registration 2019-12-02 18:19:51 +01:00
Johannes Zellner f2fca33309 Add support to upload custom profile avatar 2019-12-02 18:03:54 +01:00
Johannes Zellner 14d26fe064 Do not crash on migration
A bit late but still
2019-12-02 18:03:54 +01:00
Girish Ramakrishnan 9cc968e790 Pass the new data dir as a task argument 2019-11-25 14:22:27 -08:00
Girish Ramakrishnan 831e22b4ff Fix failing test 2019-11-23 18:35:15 -08:00
Girish Ramakrishnan 6774514bd2 Return args as part of task.get
the ui needs this to repair any failed app task
2019-11-23 18:06:33 -08:00
Girish Ramakrishnan f543b98764 Remove BoxError.UNKNOWN_ERROR 2019-11-22 14:27:41 -08:00
Johannes Zellner 2e94600afe Don't set 'Starting ...' as initial task progress message
This is confusing for tasks like "stop" as it will say "Starting ..."
2019-11-22 13:54:43 +01:00
Johannes Zellner 9295ce783a Other logs are lowercase 2019-11-22 12:31:41 +01:00
Johannes Zellner 134f8a28bf Hide access tokens from logs 2019-11-22 12:29:13 +01:00
Girish Ramakrishnan ab5e4e998c Fix reduce usage 2019-11-21 13:48:31 -08:00
Girish Ramakrishnan a98551f99c rename disks to system 2019-11-21 13:01:08 -08:00
Girish Ramakrishnan 42fe84152a return swap information 2019-11-21 12:55:17 -08:00
Girish Ramakrishnan 8a3d212bd4 Fix note 2019-11-20 16:17:47 -08:00
Girish Ramakrishnan af51ddc347 Fix crash when user with active session is deleted 2019-11-20 16:12:21 -08:00
Girish Ramakrishnan b582e549c2 do not unconfigure reverse proxy on container destroy 2019-11-20 15:38:55 -08:00
Girish Ramakrishnan 5efbccd974 Revert migration change since some cloudrons already got 4.3.3 2019-11-20 14:43:01 -08:00
Johannes Zellner 82f5cd6075 Remove unused stuff in external ldap tests 2019-11-20 22:30:53 +01:00
Johannes Zellner 0d8820c247 Add external ldap tests 2019-11-20 22:21:40 +01:00
Girish Ramakrishnan 37c6a96a3a s3: if etag is not present, flag as error 2019-11-20 12:53:36 -08:00
Johannes Zellner c53b54bda3 Only create external ldap users for oauth logins 2019-11-20 20:05:22 +01:00
Girish Ramakrishnan 808753ad3a CLI tokens are now valid for a month 2019-11-20 10:07:15 -08:00
Girish Ramakrishnan f919570cea Fix tests
mailboxDomain can be null (even though install/clone currently always
allocate one)
2019-11-20 09:57:51 -08:00
Johannes Zellner 9acf49a99e Fix typo 2019-11-20 18:18:21 +01:00
Johannes Zellner 239883d01f Add autoCreate flag to external ldap config 2019-11-20 18:18:21 +01:00
Johannes Zellner e3cee37527 Move autocreation logic into external ldap 2019-11-20 18:18:21 +01:00
Johannes Zellner 8fd0461c62 Auto create users on login if present in external ldap source 2019-11-20 18:18:21 +01:00
Girish Ramakrishnan 4d2b5c83ca Bump version to re-generate configs 2019-11-19 17:36:05 -08:00
Girish Ramakrishnan bc314c1119 Re-generate collectd and logrotate configs on container recreate
this was the reason graphs were not showing up properly
2019-11-19 17:28:31 -08:00
Girish Ramakrishnan d01749a2c2 Add 4.3.4 changes 2019-11-19 11:42:48 -08:00
Girish Ramakrishnan b46154676a Do not error if fallback certs went missing
This atleast lets the user remove and add the domain to fix things up
2019-11-19 09:36:35 -08:00
Girish Ramakrishnan fd2d60dca3 Match the version entirely during restore
Sometimes, we introduce migrations in patch releases and this causes
problems when restoring the sql dump
2019-11-18 15:05:01 -08:00
Girish Ramakrishnan ed17bdc7c3 typo 2019-11-17 21:16:36 -08:00
Girish Ramakrishnan ac05399cda Add changes 2019-11-17 12:29:53 -08:00
Girish Ramakrishnan 1af5c6a418 Fix registry detection
ECR registry does not have a username component
2019-11-17 11:50:58 -08:00
Girish Ramakrishnan e2bb668fe4 add note on appStoreId 2019-11-16 10:31:38 -08:00
Girish Ramakrishnan d255466417 manifest.id is optional for custom apps 2019-11-15 17:28:54 -08:00
Girish Ramakrishnan 5509406395 add mailboxDomain field to apps table 2019-11-15 09:40:35 -08:00
Girish Ramakrishnan 97333474c4 Remove delay introduced by mistake in 5c920fd20 2019-11-14 17:40:21 -08:00
Johannes Zellner 38928d63d6 node's http server has a default timeout of 2min which is too short for build bot 2019-11-14 13:15:18 +01:00
Girish Ramakrishnan 05c64dcbf2 move unbound config to separate file 2019-11-13 14:48:56 -08:00
Girish Ramakrishnan e39b081567 Change restart policy to unless-stopped 2019-11-13 10:29:54 -08:00
Girish Ramakrishnan 62174658cf 4.3.2 changes 2019-11-12 16:43:27 -08:00
Girish Ramakrishnan 3d26e8a666 Revert "Update cloudron-manifestformat and other modules"
This reverts commit 3d337640ef.

Only update cloudron-manifestformat
2019-11-12 14:54:16 -08:00
Girish Ramakrishnan 3d337640ef Update cloudron-manifestformat and other modules 2019-11-12 14:26:13 -08:00
Girish Ramakrishnan 985eaf8ca9 Better progress message 2019-11-11 17:09:46 -08:00
Girish Ramakrishnan e0bee13812 validate sysinfo in setup as well 2019-11-11 16:32:29 -08:00
Girish Ramakrishnan 7c6922d228 validate sysinfo configuration 2019-11-11 16:05:53 -08:00
Girish Ramakrishnan bf68c2d321 default has changed 2019-11-11 11:19:42 -08:00
Girish Ramakrishnan fd51320fb7 sysinfoConfig is now non-optional 2019-11-11 11:05:34 -08:00
Girish Ramakrishnan 815392ba38 restore: add sysinfoConfig 2019-11-11 09:49:18 -08:00
Girish Ramakrishnan f8c110f75c 4.3.1 changes 2019-11-11 09:43:19 -08:00
Girish Ramakrishnan 70f9ceb1b8 better not found message 2019-11-11 09:13:45 -08:00
Girish Ramakrishnan 2353a8b5fa list unstable apps by default 2019-11-11 08:42:00 -08:00
Girish Ramakrishnan cf1c2dc1ee Fix crash when listing mailboxes 2019-11-10 12:44:39 -08:00
Johannes Zellner 467283d5e0 Destroy all session by a user if wanted 2019-11-08 21:32:55 +01:00
Girish Ramakrishnan a887e19d46 Update mail container 2019-11-07 15:16:51 -08:00
Girish Ramakrishnan 2ab941660e Fix haraka crash
https://github.com/haraka/Haraka/issues/2732
2019-11-07 15:10:34 -08:00
Girish Ramakrishnan a75769071c remove obsolete test 2019-11-07 14:23:57 -08:00
Girish Ramakrishnan 7f2af067cf Add enums for cid 2019-11-07 13:38:33 -08:00
Girish Ramakrishnan 88454e7d6c remove unused function 2019-11-07 13:35:37 -08:00
Girish Ramakrishnan 5c920fd200 never skip password verification 2019-11-07 13:10:12 -08:00
Girish Ramakrishnan ab650c7a95 more changes 2019-11-07 11:13:52 -08:00
Girish Ramakrishnan 1e776bbbe0 Add route to get public IP 2019-11-07 10:41:15 -08:00
Girish Ramakrishnan cd0294129f Add changes 2019-11-07 09:25:04 -08:00
Johannes Zellner d1c6e786c2 Remove unused CLOUDRON_ADMIN_EMAIL 2019-11-07 16:38:30 +01:00
Girish Ramakrishnan 58d66b5293 mail: resolve list members 2019-11-06 21:45:54 -08:00
Girish Ramakrishnan 1942a7ecf4 redis: start app redis addons on image update 2019-11-06 09:38:20 -08:00
Girish Ramakrishnan 22c2add55e Update redis 2019-11-05 21:59:35 -08:00
Girish Ramakrishnan 60c5cccfc2 Add MAIL_ERROR 2019-11-05 20:55:21 -08:00
Girish Ramakrishnan b4874ec1f4 refactor getting mail auth 2019-11-05 19:54:53 -08:00
Girish Ramakrishnan d7b326bf2b clone: appdb.add must also put the reverse proxy config 2019-11-05 13:58:02 -08:00
Girish Ramakrishnan b9d8b5f973 clone: copy reverseProxyConfig 2019-11-05 12:50:30 -08:00
Girish Ramakrishnan 64fd6e0dac Allow redis with no password 2019-11-05 10:48:36 -08:00
Girish Ramakrishnan 868103e7e4 Add changes 2019-11-05 09:21:23 -08:00
Johannes Zellner 3354cb8ebe Add network interface check 2019-11-05 15:03:36 +01:00
Johannes Zellner 4fc012dea0 Fix typo in sysinfo route handler 2019-11-05 13:45:06 +01:00
Girish Ramakrishnan 947cb786d6 ldapsync: add progress callback 2019-11-04 12:05:35 -08:00
Girish Ramakrishnan 689f2791ba validate fields in testConfig 2019-10-31 11:46:00 -07:00
Girish Ramakrishnan a5ec5b0ed9 externalLdap: search and then bind 2019-10-30 15:32:49 -07:00
Girish Ramakrishnan 8e5916b785 oauth2: catch any errors in handlers 2019-10-30 15:15:36 -07:00
Girish Ramakrishnan 563f846eba style fixes 2019-10-30 14:27:58 -07:00
Girish Ramakrishnan 7781ea3205 remove this check, let if get marked as conflicting 2019-10-30 11:05:26 -07:00
Girish Ramakrishnan 2f5ece8f1d make displayName also a const 2019-10-30 11:04:19 -07:00
Girish Ramakrishnan ec46dab754 camel case 2019-10-30 11:02:21 -07:00
Girish Ramakrishnan d5d27d512c make email a constant 2019-10-30 10:59:48 -07:00
Girish Ramakrishnan 0a695190c4 Remove bindDn validation
in some AD setups, this is a email
2019-10-30 09:35:33 -07:00
Girish Ramakrishnan 59deca76a1 add changes 2019-10-30 09:16:55 -07:00
Girish Ramakrishnan a829ab44f1 sysinfo: remove the ec2 and scaleway providers
we can just use the generic one for those as well
2019-10-30 09:13:01 -07:00
Girish Ramakrishnan 82a7befb92 Fix crashes 2019-10-29 20:33:32 -07:00
Girish Ramakrishnan 331d0ee717 declare the variable 2019-10-29 20:20:35 -07:00
Girish Ramakrishnan addafa529f sysinfoConfig can be passed when provisioning 2019-10-29 20:12:37 -07:00
Girish Ramakrishnan 8232d471a3 Add route to set/get sysinfo 2019-10-29 20:08:45 -07:00
Girish Ramakrishnan 813454ca82 sysinfo: Add static and network intf providers 2019-10-29 16:12:58 -07:00
Girish Ramakrishnan 7d987d7c79 make sysinfo provider a setting 2019-10-29 15:56:50 -07:00
Girish Ramakrishnan 7a25187bee Disable invite & password reset route for external users 2019-10-29 11:03:28 -07:00
Girish Ramakrishnan f97cbb5fd5 Use private registry auth 2019-10-27 13:07:07 -07:00
Girish Ramakrishnan 12d233c5f9 provide suggestion as part of the error 2019-10-27 12:01:30 -07:00
Girish Ramakrishnan 09fce1978e Add to changes 2019-10-25 17:06:16 -07:00
Girish Ramakrishnan 8ed2f98d1d print username field as well 2019-10-25 17:00:59 -07:00
Girish Ramakrishnan 13262d014b call unbind 2019-10-25 16:58:15 -07:00
Girish Ramakrishnan ade1187fc8 ldap: more logs 2019-10-25 16:46:55 -07:00
Girish Ramakrishnan 2404e79928 ldap: do the secret key dance 2019-10-25 16:46:49 -07:00
Girish Ramakrishnan d68ed91b17 ldap: add usernameField
we need this for okta where uid is the email
2019-10-25 15:50:26 -07:00
Girish Ramakrishnan 1a21423401 ldap: add provider field 2019-10-25 15:40:22 -07:00
Girish Ramakrishnan a478134759 mail: put the type and hostname in notification 2019-10-25 10:16:17 -07:00
Girish Ramakrishnan c639746211 Update changes 2019-10-24 21:43:09 -07:00
Girish Ramakrishnan 7a96e4858a Not found messages at the db level 2019-10-24 20:48:38 -07:00
Girish Ramakrishnan 02339d503c do not re-generate DATABASE_ERROR 2019-10-24 20:31:45 -07:00
Girish Ramakrishnan c3a5360a88 Add not implemented error code 2019-10-24 18:40:37 -07:00
Girish Ramakrishnan ad9097d212 Remove various uses of INTERNAL_ERROR
INTERNAL_ERROR now means there really was some internal error
2019-10-24 18:32:36 -07:00
Girish Ramakrishnan 6e57f8cc03 Refactor toHttpError code into BoxError 2019-10-24 18:09:55 -07:00
Girish Ramakrishnan d6365ff27f Move AppstoreError to BoxError 2019-10-24 17:47:16 -07:00
Girish Ramakrishnan 4793eb9ef5 Finish UsersError removal 2019-10-24 15:19:07 -07:00
Girish Ramakrishnan 03175aa8de IN_USE -> CONFLICT
also, remove databaseerror
2019-10-24 15:07:37 -07:00
Girish Ramakrishnan bc3169deb3 Move UsersError to BoxError 2019-10-24 15:06:41 -07:00
Girish Ramakrishnan 9b4d43075e Fix some typos 2019-10-24 14:34:10 -07:00
Girish Ramakrishnan d2c12297dc Move ExternalLdapError to BoxError 2019-10-24 14:32:27 -07:00
Girish Ramakrishnan 1a8496d61e Move MailError to BoxError 2019-10-24 14:10:23 -07:00
Girish Ramakrishnan a017af41c5 Start moving db code to use BoxError as well 2019-10-24 14:09:53 -07:00
Girish Ramakrishnan ec216d9828 Add PLAN_LIMIT for now
Should remove this and make it something else
2019-10-24 11:05:36 -07:00
Girish Ramakrishnan bce1efb77c Move AppsError to BoxError 2019-10-24 10:39:47 -07:00
Girish Ramakrishnan b078d37f37 Remove REVERSEPROXY_ERROR 2019-10-24 10:31:56 -07:00
Girish Ramakrishnan 8d944f74c0 Make reverseProxy return BoxError consistently 2019-10-24 10:28:38 -07:00
Girish Ramakrishnan dc10b8a07f Move AddonsError to BoxError 2019-10-23 15:57:01 -07:00
Girish Ramakrishnan 7b9f741522 Move ProvisionError to BoxError 2019-10-23 15:45:09 -07:00
Girish Ramakrishnan 51cb3b0ba8 Move DomainsError to BoxError 2019-10-23 15:15:19 -07:00
Girish Ramakrishnan 4db4834c90 rename variable 2019-10-23 15:03:42 -07:00
Girish Ramakrishnan e1f0d12251 Fix error handling 2019-10-23 09:53:46 -07:00
Girish Ramakrishnan e2388b7d88 Move UpdaterError to BoxError 2019-10-23 09:39:26 -07:00
Girish Ramakrishnan d0e6b6bfe4 Do not re-translate to DockerError 2019-10-23 09:30:05 -07:00
Girish Ramakrishnan b6f2c94464 test registry config 2019-10-23 06:49:29 -07:00
Girish Ramakrishnan 8cdddef077 Add registry config to settings table 2019-10-22 22:56:25 -07:00
Girish Ramakrishnan e82ac5ecc5 Ensure docker code returns BoxError 2019-10-22 21:46:32 -07:00
Girish Ramakrishnan db6c07f86a Move ReverseProxyError with BoxError 2019-10-22 21:24:31 -07:00
Girish Ramakrishnan 2df642000d Move ClientsError to BoxError 2019-10-22 21:16:49 -07:00
Girish Ramakrishnan 11d80cec7d Fix mailbox tests 2019-10-22 21:03:47 -07:00
Girish Ramakrishnan 8c9ce30d29 Move BackupsError to BoxError 2019-10-22 21:03:47 -07:00
Girish Ramakrishnan df142994a8 Move TaskError into BoxError 2019-10-22 21:03:47 -07:00
Girish Ramakrishnan 2d115d3d0f Move GroupsError to BoxError 2019-10-22 16:34:17 -07:00
Girish Ramakrishnan 1b594d3e50 Remove unused GroupsError 2019-10-22 16:26:38 -07:00
Girish Ramakrishnan 332f2e7c10 Move SysInfoError to BoxError 2019-10-22 14:09:44 -07:00
Girish Ramakrishnan a7614cef2e Move CloudronError to BoxError 2019-10-22 14:06:19 -07:00
Girish Ramakrishnan 9842b6d4a1 Move EventLogError to BoxError 2019-10-22 13:59:01 -07:00
Girish Ramakrishnan 88818a1ec2 Move NotificationsError to BoxError 2019-10-22 13:00:10 -07:00
Girish Ramakrishnan 812f5cce99 Move DisksError to BoxError 2019-10-22 11:11:41 -07:00
Girish Ramakrishnan fdf7da9111 Move SupportError to BoxError 2019-10-22 11:08:19 -07:00
Girish Ramakrishnan ed9e1772ea move SettingsError to BoxError 2019-10-22 11:06:14 -07:00
Girish Ramakrishnan 657a2cac2f Add pagination to mailbox listing 2019-10-22 10:12:06 -07:00
Girish Ramakrishnan d15aa2744d Fix return code if start.sh is bad 2019-10-20 13:35:19 -07:00
Girish Ramakrishnan 29ab3e91b3 gcs: remove concurrency logic
this is more complicated than necessary
2019-10-18 18:54:25 -07:00
Girish Ramakrishnan f6377fd1c6 Add email_error type 2019-10-15 11:48:20 -07:00
Girish Ramakrishnan 122a987d61 4.3 changes 2019-10-15 11:40:36 -07:00
Girish Ramakrishnan 4610e78d91 Add altEmail to support ticket (when mail is down) 2019-10-15 11:39:44 -07:00
Girish Ramakrishnan 351bd46cb7 Make external backup restore a separate route (import)
fixes #650
2019-10-15 09:20:29 -07:00
Girish Ramakrishnan 8878bc4bf9 frameAncestors -> csp
It seems we cannot separate frame ancestors from CSP because the hide
header just hides everything and not a specific resource. This means
that the user has to set or unset the full policy whole sale.
2019-10-14 17:12:01 -07:00
Girish Ramakrishnan 61b6bee946 Remove unused variable 2019-10-14 16:07:45 -07:00
Girish Ramakrishnan 9997cbddb8 Do not escape as html 2019-10-14 16:03:57 -07:00
Girish Ramakrishnan 7115498f32 Send reverseProxyConfig in REST response 2019-10-14 15:57:41 -07:00
Girish Ramakrishnan 0f05c243aa Remove redundant type checking validation 2019-10-14 15:18:21 -07:00
Girish Ramakrishnan 9c12f1fe15 Add field to configure the reverse proxy
part of #596
2019-10-14 15:05:25 -07:00
Girish Ramakrishnan 7383cc4e90 email: Auto-subscribe to Spam folder 2019-10-14 14:31:39 -07:00
Girish Ramakrishnan 6466b47ada 4.3 changes 2019-10-14 14:16:43 -07:00
Girish Ramakrishnan 1856fc05d9 Add timeout for apptask as well 2019-10-14 14:16:15 -07:00
Girish Ramakrishnan a19662bdfa Add a timeout for update as well
this will send a notification if an update gets stuck
2019-10-14 13:05:12 -07:00
Girish Ramakrishnan 488763fc42 rename appconfig to nginxconfig 2019-10-13 17:08:33 -07:00
Girish Ramakrishnan 7cbe60a484 Fix crash when only udp ports are defined 2019-10-11 20:39:03 -07:00
Girish Ramakrishnan ded9a6e377 Revert "remove unused function"
This reverts commit a19205e3ad.
2019-10-11 20:30:30 -07:00
Girish Ramakrishnan ea205363a0 More 4.2.7 changes 2019-10-11 20:23:33 -07:00
Girish Ramakrishnan ad13445c93 Revert "apptask: backupId/format is not part of install anymore"
This reverts commit 49e5c60422.
2019-10-11 20:21:48 -07:00
Girish Ramakrishnan eb5c2ed30b notify failed backups
fixes #649
2019-10-11 19:54:15 -07:00
Girish Ramakrishnan bd3080a6b3 lint 2019-10-11 19:54:15 -07:00
Girish Ramakrishnan be5290c5ca Add error code for timeout 2019-10-11 19:54:15 -07:00
Girish Ramakrishnan 43fd207164 Kill backup task after 12 hours
this will automatically notify by email

part of #649
2019-10-11 19:13:39 -07:00
Girish Ramakrishnan 34c53694a0 Add timeout option when starting task
Part of #649
2019-10-11 19:13:39 -07:00
Girish Ramakrishnan 927f8483ce 4.2.7 changes 2019-10-11 18:43:39 -07:00
Girish Ramakrishnan a19205e3ad remove unused function 2019-10-07 22:10:02 -07:00
Girish Ramakrishnan 49e5c60422 apptask: backupId/format is not part of install anymore 2019-10-07 15:29:18 -07:00
Girish Ramakrishnan 57b623ee44 Fix install with backupId 2019-10-07 15:01:00 -07:00
Girish Ramakrishnan 0c904af927 tpyo 2019-10-03 15:25:52 -07:00
Girish Ramakrishnan 9cd025972c Try acme flow 3 times 2019-10-03 14:47:18 -07:00
Girish Ramakrishnan 21111eccc4 retry downloadCertificate 2019-10-03 14:37:12 -07:00
Girish Ramakrishnan 917079f341 Add error message to network error 2019-10-03 14:33:49 -07:00
Girish Ramakrishnan 4d6d768be1 Append apptask logs 2019-10-03 12:20:15 -07:00
Girish Ramakrishnan c54cd992ca Validate the location passed in repair route 2019-10-03 12:08:05 -07:00
Girish Ramakrishnan d5ec599dd1 repair can always be called
this is because sometimes cloudron thinks there is no error, but there is
2019-10-03 11:30:00 -07:00
Girish Ramakrishnan 0542ab16d4 If cert renewal failed, continue using old cert 2019-10-03 11:11:02 -07:00
Girish Ramakrishnan 7e75ef7685 cert: add more debugs 2019-10-03 10:36:57 -07:00
Johannes Zellner f296265461 Add changes 2019-10-03 16:31:01 +02:00
Johannes Zellner fb4eade215 Location in configure route may be an empty string 2019-10-03 16:23:01 +02:00
Johannes Zellner 8b3e85907c Add 4.2.5 changes 2019-10-02 18:41:42 +02:00
Johannes Zellner ca4876649d The demo setting didn't go well 2019-10-02 18:39:06 +02:00
Johannes Zellner 7ebc2abe5d Add 4.2.4 changes 2019-10-02 14:15:46 +02:00
Johannes Zellner 37e132319b Ensure demo setting is '' or 'enabled' 2019-10-02 12:58:32 +02:00
Johannes Zellner b2728118e9 Remove unused require 2019-10-02 12:13:18 +02:00
Girish Ramakrishnan c428f649aa typo 2019-10-01 14:40:24 -07:00
Girish Ramakrishnan 7baf979a59 Fix verbose logs 2019-10-01 14:39:40 -07:00
Girish Ramakrishnan ccecaca047 Fix crash 2019-10-01 14:04:39 -07:00
Girish Ramakrishnan c7ee684f25 Fix bug where nginx was not reloaded on cert renewal
Looks like it worked so far because nginx got reloaded in situations
like apptask or server reboot.
2019-10-01 11:25:57 -07:00
Girish Ramakrishnan 52156c9a35 Remove unused type field 2019-10-01 11:17:12 -07:00
Girish Ramakrishnan 4fba216af9 scaleway: try to keep part numbers low 2019-09-30 20:42:37 -07:00
Girish Ramakrishnan 1d00c788d1 Remove dead code 2019-09-30 15:54:18 -07:00
Girish Ramakrishnan d891d39587 reverseproxy: rename to writeDefaultConfig 2019-09-30 15:28:05 -07:00
Girish Ramakrishnan cfde6e31ad reverseproxy: improve the note 2019-09-30 15:25:53 -07:00
Girish Ramakrishnan 243772d1f5 reverseproxy: do not export reload 2019-09-30 15:23:53 -07:00
Girish Ramakrishnan 1c36b8eaf7 Add debugs 2019-09-30 11:52:23 -07:00
Girish Ramakrishnan 120fa4924a Remove confusing isInstalling usage 2019-09-30 09:58:13 -07:00
Girish Ramakrishnan c3c9c2f39a Always pass restoreConfig for the restore case 2019-09-30 09:47:14 -07:00
Girish Ramakrishnan fc90829ba2 repair: Use backupId only if passed in via REST API 2019-09-30 09:13:13 -07:00
Girish Ramakrishnan ce9224c690 Set the domain and subdomain in details 2019-09-27 14:42:18 -07:00
Girish Ramakrishnan 18a2107247 Attach fqdn information consistently in the eventlog 2019-09-27 11:50:22 -07:00
Girish Ramakrishnan f13d05dad7 Update changes 2019-09-27 11:09:50 -07:00
Girish Ramakrishnan 86586444a9 Validate alternate domain
this also sets up fqdn in the eventlog entries
2019-09-27 10:58:59 -07:00
Girish Ramakrishnan 4e47d0595d Remove ACTION_BACKUP_CLEANUP_START 2019-09-27 09:43:40 -07:00
Girish Ramakrishnan 45e85e4d53 Set overwriteDns to be true when re-configuring 2019-09-26 22:30:58 -07:00
Girish Ramakrishnan a3420f885d Fix use of skipBackup
also, store it in the eventlog
2019-09-26 20:18:49 -07:00
Girish Ramakrishnan a266fe13d0 Remove skipNotification flag
we always want a update finish eventlog. Otherwise, the eventlog seems
strange since it says 'started updated' but didn't finish
2019-09-26 20:06:14 -07:00
Girish Ramakrishnan 44aba5d6e1 Add changes 2019-09-26 15:00:00 -07:00
Girish Ramakrishnan 3fe5307ae3 Migrate PROVIDER from cloudron.conf correctly 2019-09-26 14:19:25 -07:00
Girish Ramakrishnan d03fb0e71f Add separate flags for skipping backup and notification 2019-09-26 13:06:15 -07:00
Girish Ramakrishnan d9723b72e4 Replace Acme2Error with BoxError 2019-09-25 14:13:10 -07:00
Girish Ramakrishnan 6ba61f1bda Update changes 2019-09-25 10:30:54 -07:00
Girish Ramakrishnan d1df647ddd Another migration typo 2019-09-25 10:22:43 -07:00
Girish Ramakrishnan 95c4a1f90c Handle db migration failure 2019-09-25 10:17:02 -07:00
Girish Ramakrishnan e00325e694 typo 2019-09-25 10:06:48 -07:00
Girish Ramakrishnan 85c13cae58 Fix platform update logic 2019-09-24 21:21:49 -07:00
Girish Ramakrishnan 00fd9e5b7f add note that db upgrade code path is br0ken 2019-09-24 20:05:55 -07:00
Girish Ramakrishnan dde81ee847 lint 2019-09-24 19:50:24 -07:00
Girish Ramakrishnan c46fc96500 stash the taskId instead of args 2019-09-24 13:06:13 -07:00
Girish Ramakrishnan 1914a9a703 Capitalize the Denied to be in sync with boxerror 2019-09-24 00:58:49 -07:00
Girish Ramakrishnan 1a061e4446 Only check installationState to resume tasks
also, make resumeTasks go via app logic to capture end of task
2019-09-24 00:37:29 -07:00
Girish Ramakrishnan 29ce80cebe Use DOCKER_ERROR for docker errors 2019-09-23 23:27:32 -07:00
Girish Ramakrishnan 4b6ac538ac clear the timeout just to be safe 2019-09-23 18:17:05 -07:00
Girish Ramakrishnan 70b9000b0e Fix various app state issues
Tasks initiated by repair/uninstall can run from any state
Clear taskId for failed uninstall
Clone/install can only be run on new entries
2019-09-23 18:07:12 -07:00
Girish Ramakrishnan 24dcb1b79c typo 2019-09-23 15:31:09 -07:00
Johannes Zellner 384915883f Add namecheap token error changes 2019-09-23 23:49:43 +02:00
Johannes Zellner 4cfc75f1d1 Handle namecheap ACCESS_DENIED 2019-09-23 23:35:29 +02:00
Girish Ramakrishnan c49cbb524d Design the route for pre-flight 2019-09-23 14:34:29 -07:00
Girish Ramakrishnan b401c3d930 Make schedule task take the command as arg 2019-09-23 14:18:14 -07:00
Girish Ramakrishnan 890a7cfb37 runState is not used for task anymore 2019-09-23 13:49:38 -07:00
Girish Ramakrishnan 70a1ef1af3 Can call uninstall as long as no task is active 2019-09-23 13:32:52 -07:00
Girish Ramakrishnan 38a0cdc0be Make the repair re-issue the install command
Otherwise, repairing a repair has to be handled
2019-09-23 13:21:25 -07:00
Girish Ramakrishnan 93344a5a4a Use 424 for access denied 2019-09-23 13:10:16 -07:00
Girish Ramakrishnan 9f792fc04b Remove DockerError 2019-09-23 12:20:25 -07:00
Girish Ramakrishnan 7cb95faacb typo 2019-09-23 10:49:50 -07:00
Girish Ramakrishnan bf122f0f56 Allow uninstall in error state 2019-09-23 10:40:22 -07:00
Girish Ramakrishnan 78e9446a05 Refactor repair into separate function 2019-09-23 10:15:58 -07:00
Girish Ramakrishnan 138e1595fa make legacy error messages as JSON 2019-09-23 09:13:43 -07:00
Johannes Zellner 37b02ad36a oldConfig is only used for location configure now 2019-09-23 16:24:18 +02:00
Johannes Zellner 02f0055594 On repair oldConfig might not come as an argument 2019-09-23 16:18:44 +02:00
Girish Ramakrishnan ec1f0f9320 Do not do dataDir move in configure 2019-09-23 07:15:36 -07:00
Girish Ramakrishnan bfe6389f62 oldConfig is optional in configure 2019-09-23 07:13:51 -07:00
Johannes Zellner 30db3e8973 Fix typo in sql query 2019-09-23 15:01:20 +02:00
Johannes Zellner 5b67f2cf29 Add apt packages required for samba mounts
The linux-generic package is anyways important for ensuring extra
modules will be rebuilt during updates.
2019-09-23 12:21:37 +02:00
Girish Ramakrishnan a007b74b1c Do no reparse null 2019-09-22 23:08:07 -07:00
Girish Ramakrishnan a89482d4fa Send the error as a task argument
Note that if apptask dies, we will automatically restart the repair
task with the args.
2019-09-22 22:50:35 -07:00
Girish Ramakrishnan 0cd4f133aa Do not configure/restore errored apps automatically 2019-09-22 22:44:28 -07:00
Girish Ramakrishnan e5ba4ff973 Handle legacy app error message
For apps in errored state, from a previous version of Cloudron
2019-09-22 22:25:34 -07:00
Girish Ramakrishnan ce133b997d Make runState non-nullable 2019-09-22 22:07:47 -07:00
Girish Ramakrishnan 217632354f Allow repair in non-errored state 2019-09-22 21:56:07 -07:00
Girish Ramakrishnan 9841351190 Call configure for all the non-reinstall states 2019-09-22 09:38:36 -07:00
Girish Ramakrishnan f3341f4b7f Make start/stop just a installation code
the runState now just tracks if an app is stopped.
2019-09-22 01:01:52 -07:00
Girish Ramakrishnan ff1f448860 Fixup repair route
* Do not allow scheduling tasks in error state
* Only repair is allowed in error state
* Use the error object to track what to 'repair' (like the lastState)
* If uninstall failed, repair will do uninstall
* If move dir failed, repair will do move dir
2019-09-22 00:04:25 -07:00
Girish Ramakrishnan 37f28746fc Do not setup logrotate & collectd in update 2019-09-20 20:10:52 -07:00
Girish Ramakrishnan 9a22ba3af7 Add repair route
this is specifically for the case where some task failed and user
wants to get it back.
2019-09-19 23:13:44 -07:00
Girish Ramakrishnan 2942da78de Add TASK_ERROR reason code 2019-09-19 23:13:13 -07:00
Girish Ramakrishnan 89ff6be971 Make domain a field 2019-09-19 22:45:44 -07:00
Girish Ramakrishnan be0d7bcce1 Typo 2019-09-19 22:30:51 -07:00
Johannes Zellner 851b257678 Fix typo 2019-09-20 02:05:19 +02:00
Girish Ramakrishnan 579eacb644 Better pending state check 2019-09-19 16:42:49 -07:00
Girish Ramakrishnan f52c5b584e Fix crash when resuming stopped apps 2019-09-19 16:40:38 -07:00
Girish Ramakrishnan 8980c18deb Send back error when access denied 2019-09-19 15:24:21 -07:00
Girish Ramakrishnan b05a9ce064 Add API to get dns record 2019-09-19 15:10:27 -07:00
Girish Ramakrishnan 1974314c1f Add changes 2019-09-18 09:12:25 -07:00
Johannes Zellner 2bde023d4d Use a file based session store
This prevents login sessions to be lost after a box restart
2019-09-18 13:00:03 +02:00
Girish Ramakrishnan 3a10003246 libssl1 asking for restart prompt during install
https://unix.stackexchange.com/questions/146283/how-to-prevent-prompt-that-ask-to-restart-services-when-installing-libpq-dev
https://github.com/confluentinc/castle/pull/1
https://bugs.launchpad.net/ubuntu/+source/ansible/+bug/1833013
2019-09-17 14:43:11 -07:00
Girish Ramakrishnan 1b08710b7e Update haraka 2019-09-16 14:44:57 -07:00
Girish Ramakrishnan 101d09eeb3 Typo in event log 2019-09-16 13:01:35 -07:00
Girish Ramakrishnan 00f949f156 Add overwriteDns arg to install & clone
this is useful in e2e
2019-09-16 09:31:34 -07:00
Girish Ramakrishnan adbe46d369 print all the domains being registered 2019-09-16 09:22:43 -07:00
Girish Ramakrishnan 3198926cd6 return null for default dataDir 2019-09-15 22:06:03 -07:00
Girish Ramakrishnan 957a6a20fe mail: fix sieve + mail relay 2019-09-13 17:01:36 -07:00
Girish Ramakrishnan 94f75bb0d7 Update mail container for queue rework 2019-09-13 14:39:48 -07:00
Girish Ramakrishnan 0f442755e5 mail: add SRS support for mail forwarding
Fixes #637
2019-09-13 10:22:49 -07:00
Girish Ramakrishnan cd2e782d48 Make mail tests work 2019-09-12 13:59:31 -07:00
Girish Ramakrishnan e97606ca87 Remove internal sysadmin server
this is now unused
2019-09-12 13:33:01 -07:00
Girish Ramakrishnan 00ada80230 Add mail container changes 2019-09-11 15:03:57 -07:00
Girish Ramakrishnan 34db98c489 validate email in addList 2019-09-11 14:36:10 -07:00
Girish Ramakrishnan 110695355c Make mailing list members fully qualified
Part of #637
2019-09-11 12:51:57 -07:00
Girish Ramakrishnan 021fb4bb94 Add skysilk provider 2019-09-11 09:14:04 -07:00
Girish Ramakrishnan dea033e4b0 Fix comment 2019-09-10 15:43:16 -07:00
Girish Ramakrishnan 7dfe40739e Remove apps.getAppConfig 2019-09-10 15:41:35 -07:00
Girish Ramakrishnan 9f0d1b515c Add param to overwrite DNS 2019-09-10 15:41:32 -07:00
Girish Ramakrishnan 2691d46d50 migrate: only pass the old data dir 2019-09-10 15:15:20 -07:00
Girish Ramakrishnan 78c8f1de71 Add specific installation states to help out UI 2019-09-10 14:25:12 -07:00
Girish Ramakrishnan d27ee4bfbc More changes 2019-09-10 14:20:16 -07:00
Girish Ramakrishnan cc5daa428d Fix location change event log 2019-09-10 13:57:58 -07:00
Girish Ramakrishnan 3e2189aeed Remove obsolete configure route 2019-09-09 22:08:08 -07:00
Girish Ramakrishnan 79f9963792 Add robotsTxt tests 2019-09-09 21:52:01 -07:00
Girish Ramakrishnan 6f53723169 test data dir migration 2019-09-09 21:25:39 -07:00
Girish Ramakrishnan d8cb100fc0 Add mailbox test 2019-09-09 16:34:48 -07:00
Girish Ramakrishnan 5f9b2f1159 Add env and debug mode tests 2019-09-09 15:46:29 -07:00
Girish Ramakrishnan 801ca7eda1 Break down the configure route 2019-09-09 14:35:52 -07:00
Girish Ramakrishnan 45a2d3745c Fix app eventlog 2019-09-06 14:47:44 -07:00
Girish Ramakrishnan 551fe4d846 Use BoxError everywhere in apptask 2019-09-06 11:33:24 -07:00
Girish Ramakrishnan 791981c2f2 trim the provider 2019-09-06 10:24:19 -07:00
Girish Ramakrishnan a18a620847 Add BoxError.toPlainObject 2019-09-05 21:11:38 -07:00
Girish Ramakrishnan 99e63ffc3f Use BoxError for apptask errors 2019-09-05 18:13:53 -07:00
Girish Ramakrishnan e10a6d9de5 Bump graphite for buffer size fix 2019-09-05 14:33:32 -07:00
Girish Ramakrishnan 147f16571a Handle error in graphs code 2019-09-05 14:30:21 -07:00
Girish Ramakrishnan bd1fbc4a05 typo 2019-09-05 11:42:32 -07:00
Girish Ramakrishnan 0843f78ec8 Add tasks.setCompleted
this lets us easily grep the code on where the task is completed
2019-09-05 11:29:48 -07:00
Girish Ramakrishnan 9769fbfcf2 Better message 2019-09-05 11:22:29 -07:00
Girish Ramakrishnan 7e73197eb9 Trigger re-configure of apps for collectd config 2019-09-05 09:24:37 -07:00
Girish Ramakrishnan e3964fd710 Fix crash in setUpdateSuccess 2019-09-04 16:11:59 -07:00
Girish Ramakrishnan e66961b814 merge registerSubdomains and registerAlternateDomains
also, merge unregisterSubdomain and unregisterAlternateDomains
also, fix crash where app.oldConfig is used
2019-09-03 19:55:41 -07:00
Girish Ramakrishnan 4176e5a98e Add note in schema 2019-09-03 19:04:12 -07:00
Girish Ramakrishnan 45cf8a62d1 remove obsolete comment 2019-09-03 18:52:37 -07:00
Girish Ramakrishnan b1380819ba debug taskId 2019-09-03 16:06:28 -07:00
Girish Ramakrishnan 57fa457596 Typo in error handling 2019-09-03 15:55:57 -07:00
Girish Ramakrishnan de1e218ce9 Return BAD_FIELD if dataDir conflicts 2019-09-03 15:17:48 -07:00
Girish Ramakrishnan e117ee2bef Cleanup app error codes
1. The error classes (like AppsError) now take a 3rd argument details.
We can attach anything in this 3rd argument and this gets sent in the
REST response as well.

2. The HttpError class is now HttpError(statusCode, errorOrMessage). If
it's an error object, it will take the message and other things which
were attached above from it and send them across. Previously, we used to
mark this case an internal error all the time.

3. AppsError only has generic codes now. The UI code then simply checks
for additional information that we attached to show errors. For example,
BAD_FIELD will have a field: 'xx' indicating which field is at fault.
ALREADY_EXISTS has information on which domain or port caused a problem.
The advantage here is we can drop all these error codes that are
specific to each model code.

4. Maybe some day, we can remove all these error classes and have only
one generic class. AppsError right now is pretty generic already. We can
use that error code everywhere... No need to translate errors also
everywhere.

5. Finally, in the router code, I have this function toHttpError (in
apps.js) which is also so much cleaner than what we have now. We keep
writing the same stuff over and over.
2019-09-03 10:39:02 -07:00
Girish Ramakrishnan a9e101d9f4 Add note on why it is BAD_STATE 2019-09-02 13:55:43 -07:00
Girish Ramakrishnan a2f8203a42 Add location conflict error code 2019-09-02 12:42:28 -07:00
Girish Ramakrishnan b9ee127775 Send detail in apps error 2019-09-02 12:41:32 -07:00
Girish Ramakrishnan 6668bb3e8a Handle BAD_STATE as well 2019-09-02 12:17:48 -07:00
Girish Ramakrishnan 5fd129e509 send reason code as part of details 2019-09-01 21:22:46 -07:00
Girish Ramakrishnan d59c1f53b9 apps: add detail to http error messages 2019-09-01 18:35:06 -07:00
Girish Ramakrishnan d2f38c1abc Remove unused error code 2019-09-01 17:39:07 -07:00
Girish Ramakrishnan c0a1db6941 Send details as part of AppsError
the last mile module has been updated to pipe through additional properties.
2019-09-01 13:42:25 -07:00
Girish Ramakrishnan fc10b4a79b Update lastmile 2019-09-01 13:34:40 -07:00
Girish Ramakrishnan 9da2117e99 Re-enable configure/restore test 2019-08-30 16:12:35 -07:00
Girish Ramakrishnan 7e030b149b More changes 2019-08-30 15:51:50 -07:00
Girish Ramakrishnan bd23abd265 tasks: make error a json
also, handle case where we never got to handle task exit cleanly
2019-08-30 14:49:45 -07:00
Girish Ramakrishnan dd0fb8292c Move state enums to the model code 2019-08-30 13:21:51 -07:00
Girish Ramakrishnan b4cbf63519 Remove installationState contraint when settings health 2019-08-30 12:57:59 -07:00
Girish Ramakrishnan 4fd04fa349 Add proper error codes 2019-08-30 12:42:38 -07:00
Girish Ramakrishnan c22cdb8d81 Return error object in the API 2019-08-30 11:34:04 -07:00
Johannes Zellner eb963b2eb4 Add externalldap pagination 2019-08-30 20:26:09 +02:00
Girish Ramakrishnan 7d299908c9 Fix tests 2019-08-30 10:49:43 -07:00
Girish Ramakrishnan 2585282f86 errorMessage -> errorJson 2019-08-30 10:02:24 -07:00
Johannes Zellner f25d5b3304 Deliver the user account source in the profile api 2019-08-30 13:36:37 +02:00
Johannes Zellner 6e878faa8b Also sync fallbackEmail from ldap 2019-08-30 13:10:49 +02:00
Johannes Zellner 15a6cbe62b Make sure all password change input fields use the same validation pattern 2019-08-30 12:14:32 +02:00
Johannes Zellner 76b0b214ec Do not sync non-ldap users from ldap if usernames match
We might want to make that option in the future depending on use-cases
2019-08-30 10:20:04 +02:00
Johannes Zellner f5c643c960 Add some debugging logs when users are created or updated 2019-08-30 10:20:04 +02:00
Johannes Zellner ca8e0613fb Skip notifications for ldap syncer events 2019-08-30 10:20:04 +02:00
Johannes Zellner 0c9334d0d2 Ensure we wait for all user sync db actions to finish 2019-08-30 10:20:04 +02:00
Johannes Zellner 712dc97e9b Move the basic ldap argument validation 2019-08-30 10:20:04 +02:00
Johannes Zellner 4df48c97ec Ignore the bindDn user in the syncer 2019-08-30 10:20:04 +02:00
Johannes Zellner fe3ea53cda Ldap usually uses cn as displayName 2019-08-30 10:20:04 +02:00
Johannes Zellner d385c80882 Use external ldap bind for users from ldap source 2019-08-30 10:20:04 +02:00
Johannes Zellner b823213c94 Create and update users from external ldap 2019-08-30 10:20:04 +02:00
Johannes Zellner 4b86311ab9 Add user source property to schema 2019-08-30 10:20:04 +02:00
Johannes Zellner b9efa8f445 Use tasks api for external ldap syncer 2019-08-30 10:20:04 +02:00
Johannes Zellner f8db12346d Perform some basic static input validation for external ldap 2019-08-30 10:20:04 +02:00
Johannes Zellner 4d3948f81f Improve external ldap error reporting 2019-08-30 10:20:04 +02:00
Johannes Zellner 5431d50206 Also check errors when no bindDn is provided 2019-08-30 10:20:04 +02:00
Johannes Zellner 6db078c26a Handle externalldap errors correctly in settings route 2019-08-30 10:20:04 +02:00
Johannes Zellner f61e9c7f27 Catch basic protocol errors 2019-08-30 10:20:04 +02:00
Johannes Zellner 567d92ce00 Add external ldap enabled boolean flag 2019-08-30 10:20:04 +02:00
Johannes Zellner 7a6d26c5da Add settings route handler for external ldap configs 2019-08-30 10:20:04 +02:00
Johannes Zellner 046ac85177 Add initial externalldap code to validate ldap configs 2019-08-30 10:20:04 +02:00
Girish Ramakrishnan f0fd088247 Pick values from updateConfig 2019-08-29 20:50:45 -07:00
Girish Ramakrishnan 5ec0d1e691 Add to changes 2019-08-29 15:10:55 -07:00
Girish Ramakrishnan 9391a934c3 Do not update on uninstall 2019-08-29 14:38:42 -07:00
Girish Ramakrishnan bb62e6a318 clear taskId in the parent process 2019-08-29 13:43:45 -07:00
Girish Ramakrishnan 0da6539c48 Add progressCallback to run commands 2019-08-29 13:41:11 -07:00
Girish Ramakrishnan 9cf833dab2 Use taskId instead of states to check bad state
a) this is because, we have install state and run state.
b) we have to put taskId as part of the transaction to prevent race
2019-08-29 13:15:40 -07:00
Girish Ramakrishnan ed57260fcf add note on why it is a state 2019-08-29 11:07:19 -07:00
Girish Ramakrishnan c98f625c4c Make force update as task arg 2019-08-29 10:59:05 -07:00
Girish Ramakrishnan f3008064e4 Fix installation states
App operations can only be done in 'installed' or 'error' state.
If some other operation is in progress, you have to cancel it first.

This guarantees that the old app command got killed.
2019-08-29 10:14:23 -07:00
Girish Ramakrishnan 1faee00764 Better progress text when waiting for other tasks
Fixes #630
2019-08-28 22:13:50 -07:00
Girish Ramakrishnan a40505e2ee Remove pause flag, we already have platform lock 2019-08-28 22:13:50 -07:00
Girish Ramakrishnan 484202b4c6 better variable name 2019-08-28 21:31:42 -07:00
Girish Ramakrishnan 6a7fc17c60 Make restore/configure use scheduleTask 2019-08-28 15:36:50 -07:00
Girish Ramakrishnan 05d3897ae2 Make apps test work again 2019-08-28 15:30:23 -07:00
Girish Ramakrishnan 9f1210202a port taskmanager to use tasks 2019-08-28 15:17:53 -07:00
Girish Ramakrishnan be6b172d6f Remove app task eventlog 2019-08-28 13:24:05 -07:00
Girish Ramakrishnan fef9e0a5c1 Handle app task crashes 2019-08-28 13:19:47 -07:00
Girish Ramakrishnan b84b033bf3 typo 2019-08-28 12:51:00 -07:00
Girish Ramakrishnan b30ff1f55a rework task API to be two-phase
this lets us avoid this EE based API. we now add and then start
explicitly.
2019-08-28 10:39:40 -07:00
Girish Ramakrishnan c6be0b290b updateConfig is no more 2019-08-27 22:03:43 -07:00
Girish Ramakrishnan 33cfd7a629 Add 'success' virtual field to the tasks 2019-08-27 21:36:52 -07:00
Girish Ramakrishnan 5952a5c69d Send taskId in the response 2019-08-27 21:35:40 -07:00
Girish Ramakrishnan 20de563925 rename installationProgress to errorMessage 2019-08-27 20:08:35 -07:00
Girish Ramakrishnan 7da80b4c62 Ensure log directory 2019-08-27 16:36:19 -07:00
Girish Ramakrishnan 15d765be6d Comment out couple of tests 2019-08-27 16:36:19 -07:00
Girish Ramakrishnan bfe2f116a7 Make restoreConfigJson, oldConfigJson, updateConfigJson as task args 2019-08-27 16:36:15 -07:00
Girish Ramakrishnan f535b3de2f Add logFile option to startTask 2019-08-27 15:26:26 -07:00
Girish Ramakrishnan e560c18b57 apptask is not a separate process anymore 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan aecb99b6a3 Use task API in run commands 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan 7da17f8190 Use task API in app backup 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan 1964270a4f Use task API in app update 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan f45b61d95c Use task API for app restore 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan ff11c38169 Use task API for app clone 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan 3e67067431 Use task API for app uninstall 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan 824f00d1e8 Use task API for app configure 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan 96d19f59a4 Use task API for app install 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan 42c6fe50d2 Make progressCallback take an optional callback 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan 9242f7095a Migrate apptask to use tasks framework 2019-08-27 12:31:59 -07:00
Girish Ramakrishnan 99c9fbc38f add taskId to appdb 2019-08-27 12:31:55 -07:00
Girish Ramakrishnan 0d31207ad7 add taskId to tasks table 2019-08-26 14:27:29 -07:00
Girish Ramakrishnan 8af7dbc35a group -> list 2019-08-23 16:36:19 -07:00
Johannes Zellner d0a373cb15 Refresh dns records for dynamic dns setting every hour
Often home IPs change at the full hour, so we give it 5min to settle
2019-08-23 16:20:26 +02:00
Johannes Zellner 3dc87bbca8 Allow embedding pages from same origin 2019-08-22 11:37:54 +02:00
Girish Ramakrishnan a55c399585 cloudron-support: Use the PROVIDER file 2019-08-21 21:23:22 -07:00
Girish Ramakrishnan f74aa24dd2 cloudron-setup: make it work for old installs
need this for the auto-update test
2019-08-21 21:18:08 -07:00
Girish Ramakrishnan 1aa7eb4478 Collect and aggregate du information twice a day 2019-08-21 13:45:52 -07:00
Girish Ramakrishnan 0c7002ba59 settings.adminOrigin has to be loaded after setAdmin
the dangers of caching
2019-08-21 13:26:15 -07:00
Girish Ramakrishnan fd6dd1ea18 Add timestamp to the logs 2019-08-21 10:16:57 -07:00
Girish Ramakrishnan aa74d5cd82 Add a note 2019-08-20 19:47:24 -07:00
Girish Ramakrishnan 8fc10a0bdd Add note 2019-08-20 15:26:00 -07:00
Girish Ramakrishnan 809ed0f0dc clear db to stop the scheduler 2019-08-20 13:42:03 -07:00
Girish Ramakrishnan b8a4e1c4a3 Use docker for apps-test 2019-08-20 13:34:18 -07:00
Girish Ramakrishnan d9e45f732b Fix error message 2019-08-20 13:22:43 -07:00
Girish Ramakrishnan ca025b36f7 Define DockerError.EXTERNAL_ERROR 2019-08-20 13:11:06 -07:00
Girish Ramakrishnan bfb719d35e Remove use of dockerProxy 2019-08-20 11:50:54 -07:00
Girish Ramakrishnan 2a1b61107f Make the ldap test work 2019-08-20 11:45:00 -07:00
Johannes Zellner 969cee7c90 Rebuilding docker container takes a long time, so callback early
Otherwise the service restart request will just time out in the
dashboard
2019-08-20 12:12:24 +02:00
Johannes Zellner 7a3f579d3e Do not crash if a service without active docker container needs restart 2019-08-20 12:06:49 +02:00
Johannes Zellner 288d5efa88 Return DockerError instead of generic Error 2019-08-20 12:06:22 +02:00
Johannes Zellner 7be821963c Ensure we report stopped status for addons without a running docker container 2019-08-20 11:27:35 +02:00
Girish Ramakrishnan a236f8992a graphite: Fixup healthcheck url 2019-08-19 22:27:53 -07:00
Girish Ramakrishnan a5c2257f39 Update changelog 2019-08-19 19:10:30 -07:00
Girish Ramakrishnan 9d3b4ba816 store docker df output as well 2019-08-19 16:15:31 -07:00
Girish Ramakrishnan 43bf0767f1 remove docker proxy from tests
this is too complicated and also makes it hard to keep up with upstream API
2019-08-19 14:35:23 -07:00
Girish Ramakrishnan b301e5b151 Add dockerDataDisk to disks response 2019-08-19 14:14:13 -07:00
Girish Ramakrishnan 2b484c0382 collect maildata size separately 2019-08-19 13:23:31 -07:00
Johannes Zellner f40ab4e2d5 Use git+https for cloudron-io/df 2019-08-19 09:19:06 +02:00
Girish Ramakrishnan c0a27380e9 Add to changes 2019-08-18 21:59:20 -07:00
Girish Ramakrishnan 0d7a3f43c4 Collect du information 2019-08-18 21:52:41 -07:00
Girish Ramakrishnan 8195e439f3 Return all disks
We now return the disk information per app as well
2019-08-16 10:29:56 -07:00
Johannes Zellner b5edbf716c Add interox provider option 2019-08-14 14:47:08 +02:00
Johannes Zellner 466265fde1 Allow iframe embedding of cloudron.io pages 2019-08-14 14:44:12 +02:00
Girish Ramakrishnan 40033e09cd Check disk space before create app/box backups
Fixes #642
2019-08-13 10:55:02 -07:00
Johannes Zellner 573663412c Add more fuzzy user search
Fixes #646
2019-08-13 15:23:26 +02:00
Johannes Zellner 17599417f7 WIP 2019-08-13 15:16:17 +02:00
Girish Ramakrishnan 0ece6d8b0e Add dataDir to schema 2019-08-12 22:16:45 -07:00
Girish Ramakrishnan e0ac0393fe typo 2019-08-12 21:47:22 -07:00
Girish Ramakrishnan 6d38b3255c Check available disk space before update
Part of #642
2019-08-12 21:09:22 -07:00
Girish Ramakrishnan 477ff424d6 Check if we have enough disk space for docker
Part of #642
2019-08-12 20:47:53 -07:00
Girish Ramakrishnan a843104348 sftp: typo 2019-08-12 11:31:59 -07:00
Girish Ramakrishnan 0f4bc0981a graphs: fix render forwarding 2019-08-12 11:01:12 -07:00
Girish Ramakrishnan 07f6351465 Make graphite dashboard appear again 2019-08-11 22:45:11 -07:00
Girish Ramakrishnan 1b26e86365 Fix test 2019-08-10 09:37:42 -07:00
Girish Ramakrishnan 94b4bf94c0 Merge active flag into update route 2019-08-08 08:17:08 -07:00
Girish Ramakrishnan d5de05b633 Send user active flag 2019-08-08 07:19:50 -07:00
Girish Ramakrishnan 0ab6cad048 Add user enable/disable flag 2019-08-08 06:31:46 -07:00
Girish Ramakrishnan 9833ad548b Better progress message 2019-08-07 06:23:28 -07:00
Girish Ramakrishnan aa1ba3b226 Make apps-test pass 2019-08-06 10:27:19 -07:00
Girish Ramakrishnan 3774d4de28 Use API for pullImage
This allows to get proper error code

This mostly reverts commit 734286ba2e.
2019-08-06 09:46:09 -07:00
Girish Ramakrishnan e4961726bc Try to make apps-test work 2019-08-05 16:16:56 -07:00
Girish Ramakrishnan 77cf7d0da6 Bump test version 2019-08-05 06:39:16 -07:00
Girish Ramakrishnan a993e0b228 Add fullstop 2019-08-04 15:35:42 -07:00
Girish Ramakrishnan 43671a9fd6 Clear update task progress after update
Fixes #635
2019-08-04 10:21:42 -07:00
Girish Ramakrishnan 49cfd1e9b7 Add notification for box update
Fixes #634
2019-08-04 05:44:04 -07:00
Girish Ramakrishnan 58d4a4f54f quoting and fullstop 2019-08-03 10:36:38 -07:00
Girish Ramakrishnan e4e328ba6a Make user event titles better 2019-08-03 10:17:07 -07:00
Girish Ramakrishnan fd6bc955ff Remove extra line 2019-08-03 09:41:16 -07:00
Girish Ramakrishnan 511a18e0ed Display app changelog and version
part of #634
2019-08-03 09:22:13 -07:00
Girish Ramakrishnan e29d224a92 Be a bit more specific 2019-07-31 15:45:25 -07:00
Girish Ramakrishnan bb48ffb01f Fixup UA for easier detection (other than IP) 2019-07-31 15:43:15 -07:00
Girish Ramakrishnan 31fd3411f7 Add to changes 2019-07-30 15:41:03 -07:00
Girish Ramakrishnan a737d2675e Fix logrotation rules
* explicitly specify the dirs that are getting rotated
* app log rules are now moved to logrotate.ejs
* we keep task logs for a week

Some testing notes:
* touch -d "10 days ago" foo
* logrotate /etc/logrotate.conf -v to test rotation. there is a state
file created in /var/lib/logrotate/status. If we have a 'daily' rule,
it will get processed only after a log line in status exists and it's atleast
1 day old timestamp.

https://github.com/logrotate/logrotate/blob/master/logrotate.c is quite
readable
2019-07-30 15:37:15 -07:00
Girish Ramakrishnan fd462659cd tmp cleaner: only remove files and not directories
some apps like rocket.chat create directories in tmp and removing those
directories causes problems (for example, uploading)
2019-07-30 14:06:04 -07:00
Johannes Zellner cb10d0d465 Add time4vps provider 2019-07-29 20:54:41 +02:00
Girish Ramakrishnan 61f1c4884c Refactor logic so that settings.adminDomain is read in the closure 2019-07-27 19:21:49 -07:00
Girish Ramakrishnan 2cd00de6e3 initCache after every restore 2019-07-27 19:09:09 -07:00
Girish Ramakrishnan d3c5d53eae silence mysql warning 2019-07-26 22:35:44 -07:00
Girish Ramakrishnan 6dfafae342 move the comment 2019-07-26 22:19:14 -07:00
Girish Ramakrishnan 2f861c3309 specify the database 2019-07-26 22:12:40 -07:00
Girish Ramakrishnan af388f0f16 IP based restore 2019-07-26 21:37:33 -07:00
Girish Ramakrishnan c36cc86c5f init cache in various out of process workers 2019-07-26 19:38:42 -07:00
Girish Ramakrishnan 02f195b25c typo 2019-07-26 15:02:03 -07:00
Girish Ramakrishnan 18623fd9b7 cloudron.conf can be removed post migration 2019-07-26 14:55:36 -07:00
Girish Ramakrishnan 9b74bb73aa config.js is dead, long live config.js
we use settings now
2019-07-26 14:51:51 -07:00
Girish Ramakrishnan ee9636b496 move use of TEST and CLOUDRON to constants 2019-07-26 10:13:20 -07:00
Girish Ramakrishnan 5c2cbd7840 Move config.baseDir to paths 2019-07-26 10:07:08 -07:00
Girish Ramakrishnan 7fbac6cc17 typo 2019-07-26 08:44:37 -07:00
Girish Ramakrishnan 9e7e9d66bf move provider into sysinfo
this is ideally "auto-detectable" runtime information
2019-07-26 07:33:22 -07:00
Girish Ramakrishnan 7fe66aa7fa Remove unused settings.get 2019-07-25 16:31:02 -07:00
Girish Ramakrishnan 2dda0efe83 Move config.database to db code itself 2019-07-25 16:12:42 -07:00
Girish Ramakrishnan 59620ca473 config.get is dead 2019-07-25 16:08:54 -07:00
Girish Ramakrishnan 12eae1eff2 Make port a constant 2019-07-25 16:08:54 -07:00
Girish Ramakrishnan b03bf87b7d remove unused function 2019-07-25 16:08:54 -07:00
Girish Ramakrishnan c32718b164 Make ldap and docker proxy port as constants 2019-07-25 16:08:54 -07:00
Girish Ramakrishnan a6ea12fedc Make internal smtp port a constant 2019-07-25 16:08:54 -07:00
Girish Ramakrishnan 2d260eb0d5 Make sysadminPort a constant 2019-07-25 16:08:51 -07:00
Girish Ramakrishnan d7dd069ae0 Use constants.version instead of config.version 2019-07-25 15:02:14 -07:00
Girish Ramakrishnan 6a77a58489 Move hasIPv6 into sysinfo 2019-07-25 14:35:08 -07:00
Girish Ramakrishnan c30ac5f927 Add setting key names 2019-07-25 14:35:04 -07:00
Girish Ramakrishnan 437f7ef890 Migrate cloudron.conf into db 2019-07-25 14:34:16 -07:00
Girish Ramakrishnan 1f7347e8de Make custom.yml as part of the backup 2019-07-25 10:28:42 -07:00
Girish Ramakrishnan 96f59d7cfe config: edition is long gone 2019-07-24 22:32:16 -07:00
Girish Ramakrishnan d55f65c7c9 Better error message 2019-07-24 22:11:22 -07:00
Girish Ramakrishnan 9a0d5b918f totp: set window to 2
see https://github.com/speakeasyjs/speakeasy#specifying-a-window-for-verifying-hotp-and-totp

A TOTP is incremented every step time-step seconds. By default, the time-step is
30 seconds. Window of 2 means, +- 2 steps.

Fixes #633
2019-07-23 14:45:54 -07:00
Girish Ramakrishnan 3553fbc7b6 Add wasabi storage backend 2019-07-22 16:44:56 -07:00
Girish Ramakrishnan 55d53f13d9 Improve error message 2019-07-18 10:28:37 -07:00
Johannes Zellner 27369a650c Fix disk full docs link 2019-07-16 15:10:56 +02:00
Girish Ramakrishnan 913f0d5d97 Update changes file 2019-07-15 10:50:14 -07:00
Girish Ramakrishnan ada63ec697 Add app.adminEmail 2019-07-12 14:29:35 -07:00
Girish Ramakrishnan 117f06e971 Fix issue where tar backups with files > 8GB was corrupt
Fixes #640
2019-07-10 14:58:54 -07:00
Johannes Zellner 9f03a9a6e2 Do first a patch release 2019-07-03 19:14:00 +02:00
Johannes Zellner ce406c7088 Add groupCount to backendSettings 2019-07-03 13:47:23 +02:00
Girish Ramakrishnan e7127df30d remove app ownerId
this is unused
2019-07-02 21:23:51 -07:00
Girish Ramakrishnan 10e2817257 Add 4.2 changes 2019-07-02 19:24:49 -07:00
Girish Ramakrishnan 337a47c62b typo 2019-07-02 16:46:14 -07:00
Girish Ramakrishnan 14bdac20ef cloudron-support: add --admin-login 2019-07-02 16:37:46 -07:00
Girish Ramakrishnan 88e2b3f9aa Add userCount to backend settings 2019-07-01 23:26:43 -07:00
Girish Ramakrishnan 22d731f06d Fix LDAP not accessible via scheduler containers
Check the IP address against scheduler containers as well
2019-06-27 16:12:09 -07:00
Girish Ramakrishnan e3d288ef7d Add MONGODB_OPLOG_URL for apps that require oplog access
remove the replicaSet arg (it causes problems in tests but not in apps).
it causes some issues because of hostname not being set properly/docker network.
this only prevents the client from using replicaSet features which doesn't apply
to us since it is single instance.
2019-06-27 13:19:59 -07:00
Girish Ramakrishnan 455f597543 Add changes 2019-06-26 21:40:03 -07:00
Girish Ramakrishnan 8c9e626920 Remove twitter and chat link from the login footer 2019-06-26 21:39:07 -07:00
Girish Ramakrishnan 5a000c1ff4 Add MONGODB_REPLICA_SET for mongodb addon
This can be useful for constructing the ?replSet= part of the URI.
replicaSet is used by the client to discover the secondaries and fallback
automatically. if not provided, they just talk to primary.
2019-06-26 21:29:43 -07:00
Girish Ramakrishnan ddf634bfb2 o2 has stopped working 2019-06-26 18:40:07 -07:00
Girish Ramakrishnan 89d3b8cc6a Make hostname more explicit 2019-06-26 14:21:47 -07:00
Girish Ramakrishnan 49af6d09a2 CLOUDRON_APP_HOSTNAME should be the app id always
name is the container name which is "unique"
2019-06-26 14:21:43 -07:00
Girish Ramakrishnan e5b0cac284 Clarify comment 2019-06-26 14:13:26 -07:00
Girish Ramakrishnan 6f33900f85 Fix failing test 2019-06-21 15:05:28 -07:00
Girish Ramakrishnan 514823af7d More changes 2019-06-21 13:34:24 -07:00
Girish Ramakrishnan 65b058f563 More changes 2019-06-21 11:12:25 -07:00
Girish Ramakrishnan 7c8560deff Ensure redis addon vars are replaced with manifest v2 2019-06-20 23:43:18 -07:00
Girish Ramakrishnan 6bbe2613b4 Return 412 for bad password 2019-06-20 16:44:53 -07:00
Girish Ramakrishnan 5771478e4b Use 412 for invalid token, otherwise user gets logged out 2019-06-20 16:37:16 -07:00
Girish Ramakrishnan e13030bc89 fontawesome location has changed 2019-06-20 16:27:27 -07:00
Girish Ramakrishnan 0a0ac93a55 Use pattern match instead for handling v1 to v2 upgrades 2019-06-20 11:59:02 -07:00
Girish Ramakrishnan 214fb50e74 Add 4.1.5 changes 2019-06-20 11:49:39 -07:00
Girish Ramakrishnan 959f8ee31e Ensure passwords are preserved with v2 manifest 2019-06-20 11:46:50 -07:00
Girish Ramakrishnan cb0d75be37 Add changes 2019-06-19 09:19:05 -07:00
Girish Ramakrishnan 11353e9e3a DO DNS API break means this value must atleast be 30
(cherry picked from commit c0c5561aac)
2019-06-17 20:13:32 -07:00
Girish Ramakrishnan 8cd5c15c2b Fix mail auth with manifest v2 2019-06-17 11:13:59 -07:00
Girish Ramakrishnan b86b8b8ee1 4.1.4 changes
(cherry picked from commit 73a44d1fb2)
2019-06-16 17:59:18 -07:00
Girish Ramakrishnan c5f6e6b028 Prefix mysql url/database variables 2019-06-15 10:06:51 -07:00
Girish Ramakrishnan 592d8abc58 Roll back async package
something is broken, not sure what
2019-06-14 16:24:41 -07:00
Girish Ramakrishnan d93068fc62 Update package lock 2019-06-14 15:32:11 -07:00
Girish Ramakrishnan a864af52df Update packages 2019-06-14 15:31:13 -07:00
Johannes Zellner 1eedd4b185 Send changelog for updated app notifications 2019-06-12 17:15:02 +02:00
Johannes Zellner 9d38edfe95 Update the emaildevelop test template data 2019-06-12 17:14:58 +02:00
Johannes Zellner f895ebba73 Add some changes 2019-06-12 10:13:36 +02:00
Girish Ramakrishnan 511287b16e linter likes this better 2019-06-11 12:32:15 -07:00
Johannes Zellner 530e06ec66 Add changes 2019-06-11 20:33:56 +02:00
Johannes Zellner 9cab383b43 Namecom does not support @ for naked domain anymore 2019-06-11 20:33:56 +02:00
Girish Ramakrishnan 9785ab82ed Use cloudron as prefix instead of suffix 2019-06-11 09:39:45 -07:00
Johannes Zellner 9d237e7bd6 Fix sudo installation on scaleway 2019-06-11 13:30:15 +02:00
Girish Ramakrishnan 7e9885012d vary dkim selector per mail domain
this is required for the case where the domain is added on multiple
cloudrons. initially, the plan was to just vary this as a derivation
of the dashboard domain. but this will break existing installation (wildcard
and manual domain setups cannot be re-programmed automatically).
2019-06-10 18:35:38 -07:00
Girish Ramakrishnan 1de785d97c cloudron-support: add ip addr output
with cloudflare dns, we don't have ip to login
2019-06-10 09:31:34 -07:00
Girish Ramakrishnan 2bd6566537 clear timeout when get services status 2019-06-09 22:20:35 -07:00
Girish Ramakrishnan 88fa4cf188 remove reserved 'api' location
this is unused and we have no plans to use it.
2019-06-09 18:16:31 -07:00
Girish Ramakrishnan b26167481e Make the dkim private keys readable
https://forum.cloudron.io/topic/1675/dkim-and-dmarc-for-built-in-outgoing-mail/25
2019-06-06 14:45:42 -07:00
Girish Ramakrishnan 1b6af9bd12 scaleway: add tzdata
the bionic image is missing this and only has UTC in the output
of timedatectl list-timezones
2019-06-06 12:42:07 -07:00
Girish Ramakrishnan 0159963cb0 More changes 2019-06-06 11:43:24 -07:00
Girish Ramakrishnan 996041cabc add mechanism to whitelist and blacklist apps 2019-06-06 11:42:42 -07:00
Girish Ramakrishnan cb0352e33c Do not remove existing custom.yml 2019-06-06 11:24:19 -07:00
Johannes Zellner 3169f032c8 Return correct conflicting domain 2019-06-05 19:54:15 +02:00
Girish Ramakrishnan 5ff8ee1a8f Check manifest version when installing an app
This should have been done for manifest v1 already. For now, apps
will have to put in a minBoxVersion.
2019-06-03 14:02:47 -07:00
Girish Ramakrishnan d3f31a3ace Ensure all env vars have the CLOUDRON_ prefix
this is currently injected based on the manifest version (i.e v2)
2019-06-03 13:45:35 -07:00
Girish Ramakrishnan ac7e7f0db9 Set name as the network alias for app containers
this allows scheduler containers to reach app containers by http

https://forum.cloudron.io/topic/1082/bitwarden-self-hosted-password-manager
2019-06-01 10:48:51 -07:00
Girish Ramakrishnan 4c1e967dad give containers a hostname
this only affects the hostname and not the network name/alias
2019-06-01 10:02:26 -07:00
Girish Ramakrishnan f3ccd5c074 More changes 2019-06-01 09:05:48 -07:00
Girish Ramakrishnan 8369c0e2c0 4.1.2 changes 2019-05-31 12:52:57 -07:00
Girish Ramakrishnan 122a966e72 No exclamation 2019-05-30 12:17:47 -07:00
Girish Ramakrishnan 9c2ff2f862 fix image prune logic 2019-05-29 12:15:13 -07:00
Girish Ramakrishnan 0ba45e746b Accept incoming mail from a private relay 2019-05-29 11:33:37 -07:00
Girish Ramakrishnan 54c06cdabb support: send a result message 2019-05-28 10:04:04 -07:00
Girish Ramakrishnan 5a2e10317c remove autoconf
this was mostly for caas
2019-05-24 15:20:25 -07:00
Girish Ramakrishnan 8292d52acf Add changes 2019-05-24 11:19:22 -07:00
Girish Ramakrishnan 7d21470fc7 remove cloudron-provision
will move to separate repo
2019-05-22 22:33:41 -07:00
Girish Ramakrishnan eb0530bcba add note 2019-05-22 18:33:02 -07:00
Girish Ramakrishnan 8855092faa update changes 2019-05-22 14:39:47 -07:00
Girish Ramakrishnan 2e02a3c71e Revert "only admins have sftp access"
This reverts commit ecc9415679.

We want to support the workflow where normal users can have SFTP
access without being cloudron admins. The reason it is admin only
is because it is possible to upload/modify app code via SFTP to
then get cloudron admin credentials.

For this reason, we will fixup the apps as follows:
* Unmanaged WP - remove LDAP integration
* LAMP - remove LDAP. We will make a new major version that informs
  the user NOT to update the app if they use LDAP. In 4.1, we will
  expose the LDAP server, so they can use the public LDAP server for
  any integration.
* Managed WP - Remove SFTP. This is contential but if people want to
  really build/develop plugins then they can use Unmanaged WP for the dev
  environment.
* Surfer - no change. Can have SFTP and LDAP since code is not modifiable

In general, should also be careful then about adding SFTP access to random
apps (like say nextcloud), since this would allow normal user to access
other people's data.
2019-05-22 14:32:45 -07:00
Girish Ramakrishnan 5b5303ba7f Always return object in response 2019-05-22 10:41:34 -07:00
Girish Ramakrishnan 022a54278e Add missing error code 2019-05-22 10:41:25 -07:00
Girish Ramakrishnan 19b50dc428 do not dump values in debug
it ends up dumping the icon in logs
2019-05-22 09:38:27 -07:00
Girish Ramakrishnan e7eac003a9 cloudron-support: add ssh keys like support.js 2019-05-21 09:50:38 -07:00
Girish Ramakrishnan cc17c6b2cd cloudron-support: add set 2019-05-21 09:21:22 -07:00
Girish Ramakrishnan 23d16b07aa Add API to get original icon 2019-05-21 00:14:54 -07:00
Girish Ramakrishnan 7ecb3dd771 Fix resolution of cloudflare MX record
cf might rewrite the MX record if it deems that there is a conflict

https://support.cloudflare.com/hc/en-us/articles/360020296512-DNS-Troubleshooting-FAQ
2019-05-20 18:20:04 -07:00
Johannes Zellner e43f974d34 Rework namecheap tests 2019-05-20 22:21:20 +02:00
Girish Ramakrishnan e16cd38722 Update changes 2019-05-20 10:34:54 -07:00
Girish Ramakrishnan 9d2f81d6b9 Remove X-Frame-Options
This option is now obsolete in the standards and browsers are complaining.
This needs to move to be a CSP header but this is hard to do from outside
the app (since it has to be 'merged' with the app's existing CSP).

fixes #596
2019-05-20 10:11:52 -07:00
Johannes Zellner 3fe539436b Sinon was only used in old namecheap tests 2019-05-20 16:35:23 +02:00
Girish Ramakrishnan 76f94eb559 namecheap module is not used 2019-05-18 09:41:05 -07:00
Girish Ramakrishnan 7630ef921d Add changes 2019-05-17 14:40:33 -07:00
Girish Ramakrishnan 625127d298 add icon to configure route 2019-05-17 12:50:08 -07:00
Girish Ramakrishnan f24c4d2805 Look for a user set app icon 2019-05-17 10:14:02 -07:00
Girish Ramakrishnan 194340afa0 protect app icon route 2019-05-17 09:54:45 -07:00
Johannes Zellner fdc9639aba Deal with bad namecheap API naming convention 2019-05-16 18:03:09 +02:00
Johannes Zellner f95ec53a85 Check for namecheap response status 2019-05-16 18:03:09 +02:00
Johannes Zellner 3d425b7030 Rewrite namecheap backend to not rely on unmaintained node module 2019-05-16 18:03:09 +02:00
Girish Ramakrishnan 37c6c24e0e caas is dead 2019-05-16 08:49:08 -07:00
Girish Ramakrishnan 50bdd7ec7b mail: Remove authType when username is empty 2019-05-15 16:23:56 -07:00
Girish Ramakrishnan 769cb3e251 Update mail container 2019-05-15 15:54:51 -07:00
Girish Ramakrishnan 9447c45406 enable the gcdns test 2019-05-15 10:18:30 -07:00
Johannes Zellner 66a3962cfe Do not create notifications when apps are updated through the cli 2019-05-15 19:15:57 +02:00
Girish Ramakrishnan d145eacbaf send domain in auto-register
previously, this was done during startup and we didn't have a domain
in hand
2019-05-15 09:58:59 -07:00
Girish Ramakrishnan ed03ed7bad make changeDashboardDomain customizable 2019-05-14 19:20:45 -07:00
Girish Ramakrishnan 953b463799 4.1.0 changes 2019-05-14 18:00:34 -07:00
Johannes Zellner 6d28bb0489 4.0.3 changes
(cherry picked from commit 8686832bd1)
2019-05-14 16:17:00 -07:00
Johannes Zellner c2f464ea75 password change api now returns 400 instead of 403 2019-05-13 23:46:38 +02:00
Johannes Zellner 4c56ffc767 Add default footer content to custom.yml 2019-05-13 22:50:28 +02:00
Johannes Zellner 885aa8833c Remove password requirement for destructive rest routes 2019-05-13 22:48:33 +02:00
Johannes Zellner 63310c44c0 Ensure notifications are sorted by time descending 2019-05-13 22:05:58 +02:00
Johannes Zellner 05dd65718f Remove unused CLOUDRON_ID 2019-05-13 16:28:46 +02:00
Girish Ramakrishnan 05d3f8a667 gcs: fix crash 2019-05-12 18:05:48 -07:00
Girish Ramakrishnan 3fa45ea728 4.0.2 changes 2019-05-12 13:59:57 -07:00
Girish Ramakrishnan a7d2098f09 Add option to skip backup before update 2019-05-12 13:28:53 -07:00
Girish Ramakrishnan e1ecb49d59 gcdns: fix crash 2019-05-11 19:18:11 -07:00
Johannes Zellner 6facfac4c5 Add footer customization option 2019-05-11 13:37:43 +02:00
Girish Ramakrishnan 97d2494fe3 Make ticket body customizable 2019-05-10 17:35:47 -07:00
Girish Ramakrishnan a54be69c96 rework custom configuration 2019-05-10 16:18:43 -07:00
Girish Ramakrishnan 800e25a7a7 Fix crash because params was undefined 2019-05-10 13:07:29 -07:00
Girish Ramakrishnan c1ce2977fa custom: refactor code for defaults 2019-05-10 11:31:16 -07:00
Girish Ramakrishnan 7177f82041 use 405 when feature disabled
403 will logout the user
2019-05-10 09:50:33 -07:00
Girish Ramakrishnan ecc9415679 only admins have sftp access
this is because webterminal, upload/download etc are also only
restricted to admins. this keeps it aligned
2019-05-10 08:59:16 -07:00
Girish Ramakrishnan 23280f1635 make custom.yml empty by default 2019-05-10 07:40:56 -07:00
Girish Ramakrishnan af062631c5 Add changes 2019-05-09 17:28:59 -07:00
330 changed files with 26729 additions and 22328 deletions
+602
View File
@@ -1589,3 +1589,605 @@
* Remove spaces support
* Features implementation for customization
[4.0.1]
* Make it easier to import email
* Give SFTP access only to admins
[4.0.2]
* Fix GCDNS crash
* Add option to update without backing up
[4.0.3]
* Fix dashboard issue for non-admins
[4.1.0]
* Remove password requirement for uninstalling apps and users
* Hosting provider edition
* Enforce limits in mail container
* Fix crash when using unauthenticated relay
* Fix domain and tag filtering
* Customizable app icons
* Remove obsolete X-Frame-Options from nginx configs
* Give SFTP access based on access restriction
[4.1.1]
* Add UI hint about SFTP access restriction
[4.1.2]
* Accept incoming mail from a private relay
* Fix issue where unused addon images were not pruned
* Add UI for redirect from multiple domains
* Allow apps to be relocated to custom data directory
* Make all cloudron env vars have CLOUDRON_ prefix
* Update manifest version to 2
* Fix issue where DKIM keys were inaccessible
* Fix DKIM selector conflict when adding same domain across multiple cloudrons
* Fix name.com DNS backend issue for naked domains
* Add DigitalOcean Frankfurt (fra1) region for backup storage
[4.1.3]
* Update manifest format package
[4.1.4]
* Add CLOUDRON_ prefix to MySQL addon variables
[4.1.5]
* Make the terminal addon button inject variables based on manifest version
* Preserve addon passwords correctly when using v2 manifest
* Show error message instead of logging out user when invalid 2FA token is provided
* Ensure redis vars are renamed with manifest v2
* Add missing Scaleway Object Storage to restore UI
* Fix Exoscale endpoints in restore UI
* Reset the app icon when showing the configure UI
[4.1.6]
* Fix issue where CLOUDRON_APP_HOSTNAME was incorrectly set
* Remove chat link from the footer of login screen
* Add support for oplog tailing in mongodb
* Fix LDAP not accessible via scheduler containers
[4.1.7]
* Fix issue where login looped when admin bit was removed
[4.2.0]
* Fix issue where tar backups with files > 8GB was corrupt
* Add SparkPost as mail relay backend
* Add Wasabi storage backend
* TOTP tokens are now checked for with +- 60 seconds
* IP based restore
* Fix issue where task logs were not getting rotated correctly
* Add notification for box update
* User enable/disable flag
* Check disk space before various operations like install, update, backup etc
* Collect per app du information
* Set Cloudron specific UA for healthchecks
* Show message why an app task is 'pending'
* Rework app task system so that we can now pass dynamic arguments
* Add external LDAP server integration
[4.2.1]
* Rework the app configuration routes & UI
* Fine grained eventlog for app configuration
* Update Haraka to 2.8.24
* Set sieve_max_redirects to 64
* SRS support for mail forwarding
* Fix issue where sieve responses were not sent via the relay
* File based session store
* Fix API token error reporting for namecheap backend
[4.2.2]
* Fix typos in migration
[4.2.3]
* Remove flicker of custom icon
* Preserve PROVIDER setting from cloudron.conf
* Add Skip backup option when updating an app
* Fix bug where nginx was not reloaded on cert renewal
[4.2.4]
* Fix demo settings state regression
[4.2.5]
* Fix the demo settins fix
[4.2.6]
* Fix configuration of empty app location (subdomain)
[4.2.7]
* Fix issue where the icon for normal users was displayed incorrectly
* Kill stuck backup processes after 12 hours and notify admins
* Reconfigure email apps when mail domain is added/removed
* Fix crash when only udp ports are defined
[4.3.0]
* Add timeout to kill long running tasks in case they get stuck
* email: Auto-subscribe to Spam folder
* Allow setting a custom CSP policy
* ticket: when email is down, add a field to provide alternate contact email
* Re-work app import flow
* Add pagination and search to mailbox and mail alias listing
* Add UI and workflow to add a private registry
* Show external LDAP connector
* Network view: Allow IP address detection to be configurable
* Add support for custom docker registry
* Resolve any lists and aliases in a mailing list
* Rename Accounts view to Profile
* Add search for groups and user association UI
[4.3.1]
* Make logout from all button logout from all sessions
* List unstable apps by default
* Fix crash when listing mailboxes
[4.3.2]
* Update manifestformat module
[4.3.3]
* Fix bug where stopped containers got started on server restart
* Fix external LDAP UI and syncing
* Fix timeout being too low in docker proxy
* Make manifest.id optional for custom apps
* Fix registry detection in private images
* Make mailbox domain configurable for apps
[4.3.4]
* Do not error if fallback certs went missing
* Add 'New Apps' section to Appstore view
* Fix issue where graphs of some apps were not appearing
[4.4.0]
* Show swap in graphs
* Make avatars customizable
* Hide access tokens from logs
* Add missing '@' sign for email address in app mailbox
* Add app fqdn to backup progress message
* import: add option to import app in-place
* import: add option to import app from arbitrary backup config
* Show download progress for rsync backups
* Fix various repair workflows
* acme2: Implement post-as-get
[4.4.1]
* ami: fix AWS provider validation
[4.4.2]
* Fix crash when reporting that DKIM is not setup correctly
* Stopped apps cannot be updated or auto-updated
* eventlog: track support ticket creation and remote support status
[4.4.3]
* Add restart button in recovery section
* Fix issue where memory usage was not computed correctly
* cloudflare: support API tokens
[4.4.4]
* Fix bug where restart button in terminal was not working
* Add search field in apps view
* Make app view tags and domain filter persistent
* Add timezone UI
[4.4.5]
* Fix user listing regression in group edit dialog
* Do not show error page for 503
* Add mail list and mail box update events
* Certs of stopped apps are not renewed anymore
* Fix broken memory sliders in the services UI
* Set CPU Shares
* Update nodejs to 12.14.1
* Update MySQL addon packet size to 64M
[5.0.0]
* Show backup disk usage in graphs
* Add per-user app passwords
* Make app not responding page customizable
* Make footer customizable
* Add UI to import backups
* Display timestamps in browser timezone in the UI
* Mail eventlog and usage
* Add user roles - owner, admin, user manager and user
* Setup logrotate configs for collectd since upstream does not set it up
* mail: Add X-Envelope-To and X-Envelope-From headers for incoming mails
* linode: add object storage backend
* restore: carefully replace backup config
* spam: add default corpus and global db
[5.0.1]
* Show backup disk usage in graphs
* Add per-user app passwords
* Make app not responding page customizable
* Make footer customizable
* Add UI to import backups
* Display timestamps in browser timezone in the UI
* Mail eventlog and usage
* Add user roles - owner, admin, user manager and user
* Setup logrotate configs for collectd since upstream does not set it up
* mail: Add X-Envelope-To and X-Envelope-From headers for incoming mails
* linode: add object storage backend
* restore: carefully replace backup config
* spam: add default corpus and global db
[5.0.2]
* Show backup disk usage in graphs
* Add per-user app passwords
* Make app not responding page customizable
* Make footer customizable
* Add UI to import backups
* Display timestamps in browser timezone in the UI
* Mail eventlog and usage
* Add user roles - owner, admin, user manager and user
* Setup logrotate configs for collectd since upstream does not set it up
* mail: Add X-Envelope-To and X-Envelope-From headers for incoming mails
* linode: add object storage backend
* restore: carefully replace backup config
* spam: per mailbox bayes db and training
[5.0.3]
* Show backup disk usage in graphs
* Add per-user app passwords
* Make app not responding page customizable
* Make footer customizable
* Add UI to import backups
* Display timestamps in browser timezone in the UI
* Mail eventlog and usage
* Add user roles - owner, admin, user manager and user
* Setup logrotate configs for collectd since upstream does not set it up
* mail: Add X-Envelope-To and X-Envelope-From headers for incoming mails
* linode: add object storage backend
* restore: carefully replace backup config
* spam: per mailbox bayes db and training
[5.0.4]
* Fix potential previlige escalation because of ghost file
* linode: dns backend
* make branding routes owner only
* add branding API
* Add app start/stop/restart events
* Use the primary email for LE account
* make mail eventlog more descriptive
[5.0.5]
* Fix bug where incoming mail from dynamic hostnames was rejected
* Increase token expiry
* Fix bug in tag UI where tag removal did not work
[5.0.6]
* Make mail eventlog only visible to owners
* Make app password work with sftp
[5.1.0]
* Add turn addon
* Fix disk usage display
* Drop support for TLSv1 and TLSv1.1
* Make cert validation work for ECC certs
* Add type filter to mail eventlog
* mail: Fix listing of mailboxes and aliases in the UI
* branding: fix login page title
* Only a Cloudron owner can install/update/exec apps with the docker addon
* security: reset tokens are only valid for a day
* mail: fix eventlog db perms
* Fix various bugs in the disk graphs
[5.1.1]
* Add turn addon
* Fix disk usage display
* Drop support for TLSv1 and TLSv1.1
* Make cert validation work for ECC certs
* Add type filter to mail eventlog
* mail: Fix listing of mailboxes and aliases in the UI
* branding: fix login page title
* Only a Cloudron owner can install/update/exec apps with the docker addon
* security: reset tokens are only valid for a day
* mail: fix eventlog db perms
* Fix various bugs in the disk graphs
* Fix collectd installation
* graphs: sort disk contents by usage
* backups: show apps that are not automatically backed up in backup view
[5.1.2]
* Add turn addon
* Fix disk usage display
* Drop support for TLSv1 and TLSv1.1
* Make cert validation work for ECC certs
* Add type filter to mail eventlog
* mail: Fix listing of mailboxes and aliases in the UI
* branding: fix login page title
* Only a Cloudron owner can install/update/exec apps with the docker addon
* security: reset tokens are only valid for a day
* mail: fix eventlog db perms
* Fix various bugs in the disk graphs
* Fix collectd installation
* graphs: sort disk contents by usage
* backups: show apps that are not automatically backed up in backup view
* turn: deny local address peers https://www.rtcsec.com/2020/04/01-slack-webrtc-turn-compromise/
[5.1.3]
* Fix crash with misconfigured reverse proxy
* Fix issue where invitation links are not working anymore
[5.1.4]
* Add support for custom .well-known documents to be served
* Add ECDHE-RSA-AES128-SHA256 to cipher list
* Fix GPG signature verification
[5.1.5]
* Check for .well-known routes upstream as fallback. This broke nextcloud's caldav/carddav
[5.2.0]
* acme: request ECC certs
* less-strict DKIM check to allow users to set a stronger DKIM key
* Add members only flag to mailing list
* oauth: add backward compat layer for backup and uninstall
* fix bug in disk usage sorting
* mail: aliases can be across domains
* mail: allow an external MX to be set
* Add UI to download backup config as JSON (and import it)
* Ensure stopped apps are getting backed up
* Add OVH Object Storage backend
* Add per-app redis status and configuration to Services
* spam: large emails were not scanned
* mail relay: fix delivery event log
* manual update check always gets the latest updates
* graphs: fix issue where large number of apps would crash the box code (query param limit exceeded)
* backups: fix various security issues in encypted backups (thanks @mehdi)
* graphs: add app graphs
* older encrypted backups cannot be used in this version
* Add backup listing UI
* stopping an app will stop dependent services
* Add new wasabi s3 storage region us-east-2
* mail: Fix bug where SRS translation was done on the main domain instead of mailing list domain
* backups: add retention policy
* Drop `NET_RAW` caps from container preventing sniffing of network traffic
[5.2.1]
* Fix app disk graphs
* restart apps on addon container change
[5.2.2]
* regression: import UI
* Mbps -> MBps
* Remove verbose logs
* Set dmode in tar extract
* mail: fix crash in audit logs
* import: fix crash because encryption is unset
* create redis with the correct label
[5.2.3]
* Do not restart stopped apps
[5.2.4]
* mail: enable/disable incoming mail was showing an error
* Do not trigger backup of stopped apps. Instead, we will just retain it's existing backups
based on retention policy
* remove broken disk graphs
* fix OVH backups
[5.3.0]
* better nginx config for higher loads
* backups: add CIFS storage provider
* backups: add SSHFS storage provider
* backups: add NFS storage provider
* s3: use vhost style
* Fix crash when redis config was set
* Update schedule was unselected in the UI
* cloudron-setup: --provider is now optional
* show warning for unstable updates
* add forumUrl to app manifest
* postgresql: add unaccent extension for peertube
* mail: Add Auto-Submitted header to NDRs
* backups: ensure that the latest backup of installed apps is always preserved
* add nginx logs
* mail: make authentication case insensitive
* Fix timeout issues in postgresql and mysql addon
* Do not count stopped apps for memory use
* LDAP group synchronization
[5.3.1]
* better nginx config for higher loads
* backups: add CIFS storage provider
* backups: add SSHFS storage provider
* backups: add NFS storage provider
* s3: use vhost style
* Fix crash when redis config was set
* Update schedule was unselected in the UI
* cloudron-setup: --provider is now optional
* show warning for unstable updates
* add forumUrl to app manifest
* postgresql: add unaccent extension for peertube
* mail: Add Auto-Submitted header to NDRs
* backups: ensure that the latest backup of installed apps is always preserved
* add nginx logs
* mail: make authentication case insensitive
* Fix timeout issues in postgresql and mysql addon
* Do not count stopped apps for memory use
* LDAP group synchronization
[5.3.2]
* Do not install sshfs package
* 'provider' is not required anymore in various API calls
* redis: Set maxmemory and maxmemory-policy
* Add mlock capability to manifest (for vault app)
[5.3.3]
* Fix issue where some postinstall messages where causing angular to infinite loop
[5.3.4]
* Fix issue in database error handling
[5.4.0]
* Update nginx to 1.18 for various security fixes
* Add ping capability (for statping app)
* Fix bug where aliases were displayed incorrectly in SOGo
* Add univention as LDAP provider
* Bump max_connection for postgres addon to 200
* mail: Add pagination to mailing list API
* Allow admin to lock email and display name of users
* Allow admin to ensure all users have 2FA setup
* ami: fix regression where we didn't send provider as part of get status call
* nginx: hide version
* backups: add b2 provider
* Add filemanager webinterface
* Add darkmode
* Add note that password reset and invite links expire in 24 hours
[5.4.1]
* Update nginx to 1.18 for various security fixes
* Add ping capability (for statping app)
* Fix bug where aliases were displayed incorrectly in SOGo
* Add univention as LDAP provider
* Bump max_connection for postgres addon to 200
* mail: Add pagination to mailing list API
* Allow admin to lock email and display name of users
* Allow admin to ensure all users have 2FA setup
* ami: fix regression where we didn't send provider as part of get status call
* nginx: hide version
* backups: add b2 provider
* Add filemanager webinterface
* Add darkmode
* Add note that password reset and invite links expire in 24 hours
[5.5.0]
* postgresql: update to PostgreSQL 11
* postgresql: add citext extension to whitelist for loomio
* postgresql: add btree_gist,postgres_fdw,pg_stat_statements,plpgsql extensions for gitlab
* SFTP/Filebrowser: fix access of external data directories
* Fix contrast issues in dark mode
* Add option to delete mailbox data when mailbox is delete
* Allow days/hours of backups and updates to be configurable
* backup cleaner: fix issue where referenced backups where not counted against time periods
* route53: fix issue where verification failed if user had more than 100 zones
* rework task workers to run them in a separate cgroup
* backups: now much faster thanks to reworking of task worker
* When custom fallback cert is set, make sure it's used over LE certs
* mongodb: update to MongoDB 4.0.19
* List groups ordered by name
* Invite links are now valid for a week
* Update release GPG key
* Add pre-defined variables ($CLOUDRON_APPID) for better post install messages
* filemanager: show folder first
[5.6.0]
* Remove IP nginx configuration that redirects to dashboard after activation
* dashboard: looks for search string in app title as well
* Add vaapi caps for transcoding
* Fix issue where the long mongodb database names where causing app indices of rocket.chat to overflow (> 127)
* Do not resize swap if swap file exists. This means that users can now control how swap is allocated on their own.
* SFTP: fix issue where parallel rebuilds would cause an error
* backups: make part size configurable
* mail: set max email size
* mail: allow mail server location to be set
* spamassassin: custom configs and wl/bl
* Do not automatically update to unstable release
* scheduler: reduce container churn
* mail: add API to set banner
* Fix bug where systemd 237 ignores --nice value in systemd-run
* postgresql: enable uuid-ossp extension
* firewall: add blocklist
* HTTP URLs now redirect directly to the HTTPS of the final domain
* linode: Add singapore region
* ovh: add sydney region
* s3: makes multi-part copies in parallel
[5.6.1]
* Blocklists are now stored in a text file instead of json
* regenerate nginx configs
[5.6.2]
* Update docker to 19.03.12
* Fix sorting of user listing in the UI
* namecheap: fix crash when server returns invalid response
* unlink ghost file automatically on successful login
* Bump mysql addon connection limit to 200
* Fix install issue where `/dev/dri` may not be present
* import: when importing filesystem backups, the input box is a path
* firewall: fix race condition where blocklist was not added in correct position in the FORWARD chain
* services: fix issue where services where scaled up/down too fast
* turn: realm variable was not updated properly on dashboard change
* nginx: add splash pages for IP based browser access
* Give services panel a separate top-level view
* Add app state filter
* gcs: copy concurrency was not used
* Mention why an app update cannot be applied and provide shortcut to start the app if stopped
* Remove version from footer into the setting view
* Give services panel a separate top-level view
* postgresql: set collation order explicity when creating database to C.UTF-8 (for confluence)
* rsync: fix error while goes missing when syncing
* Pre-select app domain by default in the redirection drop down
* robots: preseve leading and trailing whitespaces/newlines
[5.6.3]
* Fix postgres locale issue
[6.0.0]
* Focal support
* Reduce duration of self-signed certs to 800 days
* Better backup config filename when downloading
* branding: footer can have template variables like %YEAR% and %VERSION%
* sftp: secure the API with a token
* filemanager: Add extract context menu item
* Do not download docker images if present locally
* sftp: disable access to non-admins by default
* postgresql: whitelist pgcrypto extension for loomio
* filemanager: Add new file creation action and collapse new and upload actions
* rsync: add warning to remove lifecycle rules
* Add volume management
* backups: adjust node's heap size based on memory limit
* s3: diasble per-chunk timeout
* logs: more descriptive log file names on download
* collectd: remove collectd config when app stopped (and add it back when started)
* Apps can optionally request an authwall to be installed in front of them
* mailbox can now owned by a group
* linode: enable dns provider in setup view
* dns: apps can now use the dns port
* httpPaths: allow apps to specify forwarding from custom paths to container ports (for OLS)
* add elasticemail smtp relay option
* mail: add option to fts using solr
* mail: change the namespace separator of new installations to /
* mail: enable acl
* Disable THP
* filemanager: allow download dirs as zip files
* aws: add china region
* security: fix issue where apps could send with any username (but valid password)
* i18n support
[6.0.1]
* app: add export route
* mail: on location change, fix lock up when one or more domains have invalid credentials
* mail: fix crash because of write after timeout closure
* scaleway: fix installation issue where THP is not enabled in kernel
[6.1.0]
* mail: update haraka to 2.8.27. this fixes zero-length queue file crash
* update: set/unset appStoreId from the update route
* proxyauth: Do not follow redirects
* proxyauth: add 2FA
* appstore: add category translations
* appstore: add media category
* prepend the version to assets when sourcing to avoid cache hits on update
* filemanger: list volumes of the app
* Display upload size and size progress
* nfs: chown the backups for hardlinks to work
* remove user add/remove/role change email notifications
* persist update indicator across restarts
* cloudron-setup: add --generate-setup-token
* dashboard: pass accessToken query param to automatically login
* wellknown: add a way to set well known docs
* oom: notification mails have links to dashboard
* collectd: do not install xorg* packages
* apptask: backup/restore tasks now use the backup memory limit configuration
* eventlog: add logout event
* mailbox: include alias in mailbox search
* proxyAuth: add path exclusion
* turn: fix for CVE-2020-26262
* app password: fix regression where apps are not listed anymore in the UI
* Support for multiDomain apps (domain aliases)
* netcup: add dns provider
* Container swap size is now dynamically determined based on system RAM/swap ratio
[6.1.1]
* Fix bug where platform does not start if memory limits could not be applied
[6.1.2]
* App disk usage was not shown in graphs
* Email autoconfig
* Fix SOGo login
+1 -1
View File
@@ -1,5 +1,5 @@
The Cloudron Subscription license
Copyright (c) 2019 Cloudron UG
Copyright (c) 2020 Cloudron UG
With regard to the Cloudron Software:
+23 -16
View File
@@ -29,9 +29,9 @@ anyone to effortlessly host web applications on their server on their own terms.
* Trivially migrate to another server keeping your apps and data (for example, switch your
infrastructure provider or move to a bigger server).
* Comprehensive [REST API](https://cloudron.io/documentation/developer/api/).
* Comprehensive [REST API](https://docs.cloudron.io/api/).
* [CLI](https://cloudron.io/documentation/cli/) to configure apps.
* [CLI](https://docs.cloudron.io/custom-apps/cli/) to configure apps.
* Alerts, audit logs, graphs, dns management ... and much more
@@ -41,30 +41,37 @@ Try our demo at https://my.demo.cloudron.io (username: cloudron password: cloudr
## Installing
You can install the Cloudron platform on your own server or get a managed server
from cloudron.io. In either case, the Cloudron platform will keep your server and
apps up-to-date and secure.
* [Selfhosting](https://cloudron.io/documentation/installation/) - [Pricing](https://cloudron.io/pricing.html)
* [Managed Hosting](https://cloudron.io/managed.html)
[Install script](https://docs.cloudron.io/installation/) - [Pricing](https://cloudron.io/pricing.html)
**Note:** This repo is a small part of what gets installed on your server - there is
the dashboard, database addons, graph container, base image etc. Cloudron also relies
on external services such as the App Store for apps to be installed. As such, don't
clone this repo and npm install and expect something to work.
## Documentation
## Development
* [Documentation](https://cloudron.io/documentation/)
This is the backend code of Cloudron. The frontend code is [here](https://git.cloudron.io/cloudron/dashboard).
## Related repos
The way to develop is to first install a full instance of Cloudron in a VM. Then you can use the [hotfix](https://git.cloudron.io/cloudron/cloudron-machine)
tool to patch the VM with the latest code.
The [base image repo](https://git.cloudron.io/cloudron/docker-base-image) is the parent image of all
the containers in the Cloudron.
```
SSH_PASSPHRASE=sshkeypassword cloudron-machine hotfix --cloudron my.example.com --release 6.0.0 --ssh-key keyname
```
## Community
## License
* [Chat](https://chat.cloudron.io)
Please note that the Cloudron code is under a source-available license. This is not the same as an
open source license but ensures the code is available for introspection (and hacking!).
## Contributions
Just to give some heads up, we are a bit restrictive in merging changes. We are a small team and
would like to keep our maintenance burden low. It might be best to discuss features first in the [forum](https://forum.cloudron.io),
to also figure out how many other people will use it to justify maintenance for a feature.
## Support
* [Documentation](https://docs.cloudron.io/)
* [Forum](https://forum.cloudron.io/)
* [Support](mailto:support@cloudron.io)
+53 -21
View File
@@ -4,8 +4,7 @@ set -euv -o pipefail
readonly SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly arg_provider="${1:-generic}"
readonly arg_infraversionpath="${SOURCE_DIR}/${2:-}"
readonly arg_infraversionpath="${SOURCE_DIR}/../src"
function die {
echo $1
@@ -14,6 +13,9 @@ function die {
export DEBIAN_FRONTEND=noninteractive
readonly ubuntu_codename=$(lsb_release -cs)
readonly ubuntu_version=$(lsb_release -rs)
# hold grub since updating it breaks on some VPS providers. also, dist-upgrade will trigger it
apt-mark hold grub* >/dev/null
apt-get -o Dpkg::Options::="--force-confdef" update -y
@@ -27,41 +29,54 @@ debconf-set-selections <<< 'mysql-server mysql-server/root_password_again passwo
# this enables automatic security upgrades (https://help.ubuntu.com/community/AutomaticSecurityUpdates)
# resolvconf is needed for unbound to work property after disabling systemd-resolved in 18.04
ubuntu_version=$(lsb_release -rs)
ubuntu_codename=$(lsb_release -cs)
gpg_package=$([[ "${ubuntu_version}" == "16.04" ]] && echo "gnupg" || echo "gpg")
apt-get -y install \
mysql_package=$([[ "${ubuntu_version}" == "20.04" ]] && echo "mysql-server-8.0" || echo "mysql-server-5.7")
apt-get -y install --no-install-recommends \
acl \
apparmor \
build-essential \
cifs-utils \
cron \
curl \
debconf-utils \
dmsetup \
$gpg_package \
ipset \
iptables \
libpython2.7 \
linux-generic \
logrotate \
mysql-server-5.7 \
nginx-full \
$mysql_package \
openssh-server \
pwgen \
resolvconf \
sudo \
swaks \
tzdata \
unattended-upgrades \
unbound \
unzip \
xfsprogs
echo "==> installing nginx for xenial for TLSv3 support"
curl -sL http://nginx.org/packages/ubuntu/pool/nginx/n/nginx/nginx_1.18.0-2~${ubuntu_codename}_amd64.deb -o /tmp/nginx.deb
# apt install with install deps (as opposed to dpkg -i)
apt install -y /tmp/nginx.deb
rm /tmp/nginx.deb
# on some providers like scaleway the sudo file is changed and we want to keep the old one
apt-get -o Dpkg::Options::="--force-confold" install -y --no-install-recommends sudo
# this ensures that unattended upgades are enabled, if it was disabled during ubuntu install time (see #346)
# debconf-set-selection of unattended-upgrades/enable_auto_updates + dpkg-reconfigure does not work
cp /usr/share/unattended-upgrades/20auto-upgrades /etc/apt/apt.conf.d/20auto-upgrades
echo "==> Installing node.js"
mkdir -p /usr/local/node-10.15.1
curl -sL https://nodejs.org/dist/v10.15.1/node-v10.15.1-linux-x64.tar.gz | tar zxvf - --strip-components=1 -C /usr/local/node-10.15.1
ln -sf /usr/local/node-10.15.1/bin/node /usr/bin/node
ln -sf /usr/local/node-10.15.1/bin/npm /usr/bin/npm
apt-get install -y python # Install python which is required for npm rebuild
mkdir -p /usr/local/node-10.18.1
curl -sL https://nodejs.org/dist/v10.18.1/node-v10.18.1-linux-x64.tar.gz | tar zxf - --strip-components=1 -C /usr/local/node-10.18.1
ln -sf /usr/local/node-10.18.1/bin/node /usr/bin/node
ln -sf /usr/local/node-10.18.1/bin/npm /usr/bin/npm
apt-get install -y --no-install-recommends python # Install python which is required for npm rebuild
[[ "$(python --version 2>&1)" == "Python 2.7."* ]] || die "Expecting python version to be 2.7.x"
# https://docs.docker.com/engine/installation/linux/ubuntulinux/
@@ -72,9 +87,9 @@ mkdir -p /etc/systemd/system/docker.service.d
echo -e "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// --log-driver=journald --exec-opt native.cgroupdriver=cgroupfs --storage-driver=overlay2" > /etc/systemd/system/docker.service.d/cloudron.conf
# there are 3 packages for docker - containerd, CLI and the daemon
curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/containerd.io_1.2.2-3_amd64.deb" -o /tmp/containerd.deb
curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce-cli_18.09.2~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker-ce-cli.deb
curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce_18.09.2~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker.deb
curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/containerd.io_1.2.13-2_amd64.deb" -o /tmp/containerd.deb
curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce-cli_19.03.12~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker-ce-cli.deb
curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce_19.03.12~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker.deb
# apt install with install deps (as opposed to dpkg -i)
apt install -y /tmp/containerd.deb /tmp/docker-ce-cli.deb /tmp/docker.deb
rm /tmp/containerd.deb /tmp/docker-ce-cli.deb /tmp/docker.deb
@@ -87,7 +102,7 @@ fi
# do not upgrade grub because it might prompt user and break this script
echo "==> Enable memory accounting"
apt-get -y --no-upgrade install grub2-common
apt-get -y --no-upgrade --no-install-recommends install grub2-common
sed -e 's/^GRUB_CMDLINE_LINUX="\(.*\)"$/GRUB_CMDLINE_LINUX="\1 cgroup_enable=memory swapaccount=1 panic_on_oops=1 panic=5"/' -i /etc/default/grub
update-grub
@@ -106,19 +121,37 @@ for image in ${images}; do
done
echo "==> Install collectd"
if ! apt-get install -y collectd collectd-utils; then
# without this, libnotify4 will install gnome-shell
apt-get install -y libnotify4 --no-install-recommends
if ! apt-get install -y --no-install-recommends libcurl3-gnutls collectd collectd-utils; then
# FQDNLookup is true in default debian config. The box code has a custom collectd.conf that fixes this
echo "Failed to install collectd. Presumably because of http://mailman.verplant.org/pipermail/collectd/2015-March/006491.html"
sed -e 's/^FQDNLookup true/FQDNLookup false/' -i /etc/collectd/collectd.conf
fi
# https://bugs.launchpad.net/ubuntu/+source/collectd/+bug/1872281
[[ "${ubuntu_version}" == "20.04" ]] && echo -e "\nLD_PRELOAD=/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so" >> /etc/default/collectd
# some hosts like atlantic install ntp which conflicts with timedatectl. https://serverfault.com/questions/1024770/ubuntu-20-04-time-sync-problems-and-possibly-incorrect-status-information
echo "==> Configuring host"
sed -e 's/^#NTP=/NTP=0.ubuntu.pool.ntp.org 1.ubuntu.pool.ntp.org 2.ubuntu.pool.ntp.org 3.ubuntu.pool.ntp.org/' -i /etc/systemd/timesyncd.conf
if systemctl is-active ntp; then
systemctl stop ntp
apt purge -y ntp
fi
timedatectl set-ntp 1
# mysql follows the system timezone
timedatectl set-timezone UTC
# Disable bind for good measure (on online.net, kimsufi servers these are pre-installed and conflicts with unbound)
echo "==> Adding sshd configuration warning"
sed -e '/Port 22/ i # NOTE: Cloudron only supports moving SSH to port 202. See https://docs.cloudron.io/security/#securing-ssh-access' -i /etc/ssh/sshd_config
# https://bugs.launchpad.net/ubuntu/+source/base-files/+bug/1701068
echo "==> Disabling motd news"
if [ -f "/etc/default/motd-news" ]; then
sed -i 's/^ENABLED=.*/ENABLED=0/' /etc/default/motd-news
fi
# Disable bind for good measure (on online.net, kimsufi servers these are pre-installed)
systemctl stop bind9 || true
systemctl disable bind9 || true
@@ -130,7 +163,7 @@ systemctl disable dnsmasq || true
systemctl stop postfix || true
systemctl disable postfix || true
# on ubuntu 18.04, this is the default. this requires resolvconf for DNS to work further after the disable
# on ubuntu 18.04 and 20.04, this is the default. this requires resolvconf for DNS to work further after the disable
systemctl stop systemd-resolved || true
systemctl disable systemd-resolved || true
@@ -139,4 +172,3 @@ systemctl disable systemd-resolved || true
ip6=$([[ -s /proc/net/if_inet6 ]] && echo "yes" || echo "no")
echo -e "server:\n\tinterface: 127.0.0.1\n\tdo-ip6: ${ip6}" > /etc/unbound/unbound.conf.d/cloudron-network.conf
systemctl restart unbound
+49 -52
View File
@@ -2,68 +2,65 @@
'use strict';
// prefix all output with a timestamp
// debug() already prefixes and uses process.stderr NOT console.*
['log', 'info', 'warn', 'debug', 'error'].forEach(function (log) {
var orig = console[log];
console[log] = function () {
orig.apply(console, [new Date().toISOString()].concat(Array.prototype.slice.call(arguments)));
};
});
require('supererror')({ splatchError: true });
let async = require('async'),
config = require('./src/config.js'),
ldap = require('./src/ldap.js'),
dockerProxy = require('./src/dockerproxy.js'),
fs = require('fs'),
ldap = require('./src/ldap.js'),
paths = require('./src/paths.js'),
proxyAuth = require('./src/proxyauth.js'),
server = require('./src/server.js');
console.log();
console.log('==========================================');
console.log(' Cloudron will use the following settings ');
console.log('==========================================');
console.log();
console.log(' Environment: ', config.CLOUDRON ? 'CLOUDRON' : 'TEST');
console.log(' Version: ', config.version());
console.log(' Admin Origin: ', config.adminOrigin());
console.log(' Appstore API server origin: ', config.apiServerOrigin());
console.log(' Appstore Web server origin: ', config.webServerOrigin());
console.log(' SysAdmin Port: ', config.get('sysadminPort'));
console.log(' LDAP Server Port: ', config.get('ldapPort'));
console.log(' Docker Proxy Port: ', config.get('dockerProxyPort'));
console.log();
console.log('==========================================');
console.log();
const NOOP_CALLBACK = function () { };
function setupLogging(callback) {
if (process.env.BOX_ENV === 'test') return callback();
var logfileStream = fs.createWriteStream(paths.BOX_LOG_FILE, { flags:'a' });
process.stdout.write = process.stderr.write = logfileStream.write.bind(logfileStream);
callback();
}
async.series([
server.start,
setupLogging,
server.start, // do this first since it also inits the database
proxyAuth.start,
ldap.start,
dockerProxy.start
], function (error) {
if (error) {
console.error('Error starting server', error);
console.log('Error starting server', error);
process.exit(1);
}
console.log('Cloudron is up and running');
});
var NOOP_CALLBACK = function () { };
process.on('SIGINT', function () {
console.log('Received SIGINT. Shutting down.');
server.stop(NOOP_CALLBACK);
ldap.stop(NOOP_CALLBACK);
dockerProxy.stop(NOOP_CALLBACK);
setTimeout(process.exit.bind(process), 3000);
});
process.on('SIGTERM', function () {
console.log('Received SIGTERM. Shutting down.');
server.stop(NOOP_CALLBACK);
ldap.stop(NOOP_CALLBACK);
dockerProxy.stop(NOOP_CALLBACK);
setTimeout(process.exit.bind(process), 3000);
// require those here so that logging handler is already setup
require('supererror');
const debug = require('debug')('box:box');
process.on('SIGINT', function () {
debug('Received SIGINT. Shutting down.');
proxyAuth.stop(NOOP_CALLBACK);
server.stop(NOOP_CALLBACK);
ldap.stop(NOOP_CALLBACK);
dockerProxy.stop(NOOP_CALLBACK);
setTimeout(process.exit.bind(process), 3000);
});
process.on('SIGTERM', function () {
debug('Received SIGTERM. Shutting down.');
proxyAuth.stop(NOOP_CALLBACK);
server.stop(NOOP_CALLBACK);
ldap.stop(NOOP_CALLBACK);
dockerProxy.stop(NOOP_CALLBACK);
setTimeout(process.exit.bind(process), 3000);
});
process.on('uncaughtException', function (error) {
console.error((error && error.stack) ? error.stack : error);
setTimeout(process.exit.bind(process, 1), 3000);
});
console.log(`Cloudron is up and running. Logs are at ${paths.BOX_LOG_FILE}`); // this goes to journalctl
});
@@ -12,8 +12,6 @@ exports.up = function(db, callback) {
db.all('SELECT * FROM users WHERE admin=1', function (error, results) {
if (error) return done(error);
console.dir(results);
async.eachSeries(results, function (r, next) {
db.runSql('INSERT INTO groupMembers (groupId, userId) VALUES (?, ?)', [ ADMIN_GROUP_ID, r.id ], next);
}, done);
@@ -1,12 +1,6 @@
'use strict';
var async = require('async'),
crypto = require('crypto'),
fs = require('fs'),
os = require('os'),
path = require('path'),
safe = require('safetydance'),
tldjs = require('tldjs');
var async = require('async');
exports.up = function(db, callback) {
db.all('SELECT * FROM apps, subdomains WHERE apps.id=subdomains.appId AND type="primary"', function (error, apps) {
@@ -0,0 +1,15 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE mail ADD COLUMN dkimSelector VARCHAR(128) NOT NULL DEFAULT "cloudron"', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE mail DROP COLUMN dkimSelector', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,14 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE apps DROP FOREIGN KEY apps_owner_constraint'),
db.runSql.bind(db, 'ALTER TABLE apps DROP COLUMN ownerId')
], callback);
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,29 @@
'use strict';
var async = require('async'),
fs = require('fs');
exports.up = function(db, callback) {
if (!fs.existsSync('/etc/cloudron/cloudron.conf')) {
console.log('Unable to locate cloudron.conf');
return callback();
}
const config = JSON.parse(fs.readFileSync('/etc/cloudron/cloudron.conf', 'utf8'));
async.series([
fs.writeFile.bind(null, '/etc/cloudron/PROVIDER', config.provider, 'utf8'),
db.runSql.bind(db, 'START TRANSACTION;'),
// we use replace instead of insert because the cloudron-setup adds api/web_server_origin even for legacy setups
db.runSql.bind(db, 'REPLACE INTO settings (name, value) VALUES(?, ?)', [ 'api_server_origin', config.apiServerOrigin ]),
db.runSql.bind(db, 'REPLACE INTO settings (name, value) VALUES(?, ?)', [ 'web_server_origin', config.webServerOrigin ]),
db.runSql.bind(db, 'REPLACE INTO settings (name, value) VALUES(?, ?)', [ 'admin_domain', config.adminDomain ]),
db.runSql.bind(db, 'REPLACE INTO settings (name, value) VALUES(?, ?)', [ 'admin_fqdn', config.adminFqdn ]),
db.runSql.bind(db, 'REPLACE INTO settings (name, value) VALUES(?, ?)', [ 'demo', config.isDemo ]),
db.runSql.bind(db, 'COMMIT')
], callback);
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,17 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users ADD COLUMN active BOOLEAN DEFAULT 1', function (error) {
if (error) return callback(error);
callback();
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN active', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,17 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE apps ADD COLUMN taskId INTEGER'),
db.runSql.bind(db, 'ALTER TABLE apps ADD CONSTRAINT apps_task_constraint FOREIGN KEY(taskId) REFERENCES tasks(id)')
], callback);
};
exports.down = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE app DROP FOREIGN KEY apps_task_constraint'),
db.runSql.bind(db, 'ALTER TABLE apps DROP COLUMN taskId'),
], callback);
};
@@ -0,0 +1,12 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps DROP updateConfigJson, DROP restoreConfigJson, DROP oldConfigJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,15 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps CHANGE installationProgress errorJson TEXT', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps CHANGE errorJson installationProgress TEXT', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,17 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users ADD COLUMN source VARCHAR(128) DEFAULT ""', function (error) {
if (error) return callback(error);
callback();
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN source', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,26 @@
'use strict';
let async = require('async');
exports.up = function(db, callback) {
db.runSql('ALTER TABLE tasks CHANGE errorMessage errorJson TEXT', [], function (error) {
if (error) console.error(error);
// convert error messages into json
db.all('SELECT id, errorJson FROM apps', function (error, apps) {
async.eachSeries(apps, function (app, iteratorDone) {
if (app.errorJson === 'null') return iteratorDone();
if (app.errorJson === null) return iteratorDone();
db.runSql('UPDATE apps SET errorJson = ? WHERE id = ?', [ JSON.stringify({ message: app.errorJson }), app.id ], iteratorDone);
}, callback);
});
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE tasks CHANGE errorJson errorMessage TEXT', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,21 @@
'use strict';
var async = require('async');
// imports mailbox entries for existing users
exports.up = function(db, callback) {
db.all('SELECT * FROM mailboxes', function (error, mailboxes) {
async.eachSeries(mailboxes, function (mailbox, iteratorDone) {
if (!mailbox.membersJson) return iteratorDone();
let members = JSON.parse(mailbox.membersJson);
members = members.map((m) => m && m.indexOf('@') === -1 ? `${m}@${mailbox.domain}` : m); // only because we don't do things in a xction
db.runSql('UPDATE mailboxes SET membersJson=? WHERE name=? AND domain=?', [ JSON.stringify(members), mailbox.name, mailbox.domain ], iteratorDone);
}, callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,19 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('UPDATE apps SET runState=? WHERE runState IS NULL', [ 'running' ], function (error) {
if (error) return callback(error);
db.runSql('ALTER TABLE apps MODIFY runState VARCHAR(512) NOT NULL', [], function (error) {
if (error) console.error(error);
callback(error);
});
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE app MODIFY runState VARCHAR(512)', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,10 @@
'use strict';
exports.up = function(db, callback) {
// We clear all demo state in the Cloudron...the demo cloudron needs manual intervention afterwards
db.runSql('REPLACE INTO settings (name, value) VALUES(?, ?)', [ 'demo', '' ], callback);
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,30 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN reverseProxyConfigJson TEXT', function (error) {
if (error) return callback(error);
db.all('SELECT id, robotsTxt FROM apps', function (error, apps) {
if (error) return callback(error);
async.eachSeries(apps, function (app, iteratorDone) {
if (!app.robotsTxt) return iteratorDone();
db.runSql('UPDATE apps SET reverseProxyConfigJson=? WHERE id=?', [ JSON.stringify({ robotsTxt: JSON.stringify(app.robotsTxt) }), app.id ], iteratorDone);
}, function (error) {
if (error) return callback(error);
db.runSql('ALTER TABLE apps DROP COLUMN robotsTxt', callback);
});
});
});
};
exports.down = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE apps DROP COLUMN reverseProxyConfigJson'),
], callback);
};
@@ -0,0 +1,13 @@
'use strict';
var fs = require('fs');
exports.up = function(db, callback) {
let sysinfoConfig = { provider: 'generic' };
db.runSql('REPLACE INTO settings (name, value) VALUES(?, ?)', [ 'sysinfo_config', JSON.stringify(sysinfoConfig) ], callback);
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,27 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE apps ADD COLUMN mailboxDomain VARCHAR(128)'),
function setDefaultMailboxDomain(done) {
db.all('SELECT * FROM apps, subdomains WHERE apps.id=subdomains.appId AND type="primary"', function (error, apps) {
if (error) return done(error);
async.eachSeries(apps, function (app, iteratorDone) {
db.runSql('UPDATE apps SET mailboxDomain=? WHERE id=?', [ app.domain, app.id ], iteratorDone);
}, done);
});
},
db.runSql.bind(db, 'ALTER TABLE apps MODIFY COLUMN mailboxDomain VARCHAR(128) NOT NULL'),
db.runSql.bind(db, 'ALTER TABLE apps ADD CONSTRAINT apps_mailDomain_constraint FOREIGN KEY(mailboxDomain) REFERENCES domains(domain)'),
], callback);
};
exports.down = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE app DROP FOREIGN KEY apps_mailDomain_constraint'),
db.runSql.bind(db, 'ALTER TABLE apps DROP COLUMN mailboxDomain'),
], callback);
};
@@ -0,0 +1,22 @@
'use strict';
let async = require('async');
exports.up = function(db, callback) {
db.runSql('SELECT * FROM domains', function (error, domains) {
if (error) return callback(error);
async.eachSeries(domains, function (domain, iteratorCallback) {
if (domain.provider !== 'cloudflare') return iteratorCallback();
let config = JSON.parse(domain.configJson);
config.tokenType = 'GlobalApiKey';
db.runSql('UPDATE domains SET configJson = ? WHERE domain = ?', [ JSON.stringify(config), domain.domain ], iteratorCallback);
}, callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,15 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN cpuShares INTEGER DEFAULT 512', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN cpuShares', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,26 @@
'use strict';
exports.up = function(db, callback) {
var cmd = 'CREATE TABLE appPasswords(' +
'id VARCHAR(128) NOT NULL UNIQUE,' +
'name VARCHAR(128) NOT NULL,' +
'userId VARCHAR(128) NOT NULL,' +
'identifier VARCHAR(128) NOT NULL,' +
'hashedPassword VARCHAR(1024) NOT NULL,' +
'creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,' +
'FOREIGN KEY(userId) REFERENCES users(id),' +
'UNIQUE (name, userId),' +
'PRIMARY KEY (id)) CHARACTER SET utf8 COLLATE utf8_bin';
db.runSql(cmd, function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('DROP TABLE appPasswords', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,22 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('DROP TABLE authcodes', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
var cmd = `CREATE TABLE IF NOT EXISTS authcodes(
authCode VARCHAR(128) NOT NULL UNIQUE,
userId VARCHAR(128) NOT NULL,
clientId VARCHAR(128) NOT NULL,
expiresAt BIGINT NOT NULL,
PRIMARY KEY(authCode)) CHARACTER SET utf8 COLLATE utf8_bin`;
db.runSql(cmd, function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,24 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('DROP TABLE clients', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
var cmd = `CREATE TABLE IF NOT EXISTS clients(
id VARCHAR(128) NOT NULL UNIQUE,
appId VARCHAR(128) NOT NULL,
type VARCHAR(16) NOT NULL,
clientSecret VARCHAR(512) NOT NULL,
redirectURI VARCHAR(512) NOT NULL,
scope VARCHAR(512) NOT NULL,
PRIMARY KEY(id)) CHARACTER SET utf8 COLLATE utf8_bin`;
db.runSql(cmd, function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,17 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE domains DROP COLUMN locked', function (error) {
if (error) return callback(error);
callback();
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE domains ADD COLUMN locked BOOLEAN DEFAULT 0', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,40 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'START TRANSACTION;'),
db.runSql.bind(db, 'ALTER TABLE users ADD COLUMN role VARCHAR(32)'),
function migrateAdminFlag(done) {
db.all('SELECT * FROM users ORDER BY createdAt', function (error, results) {
if (error) return done(error);
let ownerFound = false;
async.eachSeries(results, function (user, next) {
let role;
if (!ownerFound && user.admin) {
role = 'owner';
ownerFound = true;
console.log(`Designating ${user.username} ${user.email} ${user.id} as the owner of this cloudron`);
} else {
role = user.admin ? 'admin' : 'user';
}
db.runSql('UPDATE users SET role=? WHERE id=?', [ role, user.id ], next);
}, done);
});
},
db.runSql.bind(db, 'ALTER TABLE users DROP COLUMN admin'),
db.runSql.bind(db, 'ALTER TABLE users MODIFY role VARCHAR(32) NOT NULL'),
db.runSql.bind(db, 'COMMIT')
], callback);
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN role', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,15 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users ADD COLUMN resetTokenCreationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN resetTokenCreationTime', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,28 @@
'use strict';
let async = require('async');
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps MODIFY mailboxDomain VARCHAR(128)', [], function (error) { // make it nullable
if (error) console.error(error);
// clear mailboxName/Domain for apps that do not use mail addons
db.all('SELECT * FROM apps', function (error, apps) {
if (error) return callback(error);
async.eachSeries(apps, function (app, iteratorDone) {
var manifest = JSON.parse(app.manifestJson);
if (manifest.addons['sendmail'] || manifest.addons['recvmail']) return iteratorDone();
db.runSql('UPDATE apps SET mailboxName=?, mailboxDomain=? WHERE id=?', [ null, null, app.id ], iteratorDone);
}, callback);
});
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps MODIFY manifestJson VARCHAR(128) NOT NULL', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,17 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE mailboxes ADD COLUMN membersOnly BOOLEAN DEFAULT 0', function (error) {
if (error) return callback(error);
callback();
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN membersOnly', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,28 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE mailboxes ADD COLUMN aliasDomain VARCHAR(128)'),
function setAliasDomain(done) {
db.all('SELECT * FROM mailboxes', function (error, mailboxes) {
async.eachSeries(mailboxes, function (mailbox, iteratorDone) {
if (!mailbox.aliasTarget) return iteratorDone();
db.runSql('UPDATE mailboxes SET aliasDomain=? WHERE name=? AND domain=?', [ mailbox.domain, mailbox.name, mailbox.domain ], iteratorDone);
}, done);
});
},
db.runSql.bind(db, 'ALTER TABLE mailboxes ADD CONSTRAINT mailboxes_aliasDomain_constraint FOREIGN KEY(aliasDomain) REFERENCES mail(domain)'),
db.runSql.bind(db, 'ALTER TABLE mailboxes CHANGE aliasTarget aliasName VARCHAR(128)')
], callback);
};
exports.down = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE mailboxes DROP FOREIGN KEY mailboxes_aliasDomain_constraint'),
db.runSql.bind(db, 'ALTER TABLE mailboxes DROP COLUMN aliasDomain'),
db.runSql.bind(db, 'ALTER TABLE mailboxes CHANGE aliasName aliasTarget VARCHAR(128)')
], callback);
};
@@ -0,0 +1,15 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN servicesConfigJson TEXT', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN servicesConfigJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,15 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN bindsJson TEXT', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN bindsJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,35 @@
'use strict';
const backups = require('../src/backups.js'),
fs = require('fs');
exports.up = function(db, callback) {
db.all('SELECT value FROM settings WHERE name="backup_config"', function (error, results) {
if (error || results.length === 0) return callback(error);
var backupConfig = JSON.parse(results[0].value);
if (backupConfig.key) {
backupConfig.encryption = backups.generateEncryptionKeysSync(backupConfig.key);
backups.cleanupCacheFilesSync();
fs.writeFileSync('/home/yellowtent/platformdata/BACKUP_PASSWORD',
'This file contains your Cloudron backup password.\nBefore Cloudron v5.2, this was saved in the database.' +
'From Cloudron 5.2, this password is not required anymore. We generate strong keys based off this password and use those keys to encrypt the backups.\n' +
'This means that the password is only required at decryption/restore time.\n\n' +
'This file can be safely removed and only exists for the off-chance that you do not remember your backup password.\n\n' +
`Password: ${backupConfig.key}\n`,
'utf8');
} else {
backupConfig.encryption = null;
}
delete backupConfig.key;
db.runSql('UPDATE settings SET value=? WHERE name="backup_config"', [ JSON.stringify(backupConfig) ], callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,15 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE backups CHANGE version packageVersion VARCHAR(128) NOT NULL', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE backups CHANGE packageVersion version VARCHAR(128) NOT NULL', [], function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,24 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE backups ADD COLUMN encryptionVersion INTEGER', function (error) {
if (error) return callback(error);
db.all('SELECT value FROM settings WHERE name="backup_config"', function (error, results) {
if (error || results.length === 0) return callback(error);
var backupConfig = JSON.parse(results[0].value);
if (!backupConfig.encryption) return callback(null);
// mark old encrypted backups as v1
db.runSql('UPDATE backups SET encryptionVersion=1', callback);
});
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE backups DROP COLUMN encryptionVersion', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,18 @@
'use strict';
exports.up = function(db, callback) {
db.all('SELECT value FROM settings WHERE name="backup_config"', function (error, results) {
if (error || results.length === 0) return callback(error);
var backupConfig = JSON.parse(results[0].value);
backupConfig.retentionPolicy = { keepWithinSecs: backupConfig.retentionSecs };
delete backupConfig.retentionSecs;
// mark old encrypted backups as v1
db.runSql('UPDATE settings SET value=? WHERE name="backup_config"', [ JSON.stringify(backupConfig) ], callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,18 @@
'use strict';
exports.up = function(db, callback) {
db.all('SELECT value FROM settings WHERE name="backup_config"', function (error, results) {
if (error || results.length === 0) return callback(error);
var backupConfig = JSON.parse(results[0].value);
if (backupConfig.provider !== 'minio' && backupConfig.provider !== 's3-v4-compat') return callback();
backupConfig.s3ForcePathStyle = true; // usually minio is self-hosted. s3 v4 compat, we don't know
db.runSql('UPDATE settings SET value=? WHERE name="backup_config"', [ JSON.stringify(backupConfig) ], callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,17 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
// http://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address
async.series([
db.runSql.bind(db, 'ALTER TABLE appPasswords DROP INDEX name'),
db.runSql.bind(db, 'ALTER TABLE appPasswords ADD CONSTRAINT appPasswords_name_userId_identifier UNIQUE (name, userId, identifier)'),
], callback);
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,17 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE userGroups ADD COLUMN source VARCHAR(128) DEFAULT ""', function (error) {
if (error) return callback(error);
callback();
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE userGroups DROP COLUMN source', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,38 @@
'use strict';
const async = require('async');
exports.up = function(db, callback) {
db.runSql('ALTER TABLE backups ADD COLUMN identifier VARCHAR(128)', function (error) {
if (error) return callback(error);
db.all('SELECT * FROM backups', function (error, backups) {
if (error) return callback(error);
async.eachSeries(backups, function (backup, next) {
let identifier = 'unknown';
if (backup.type === 'box') {
identifier = 'box';
} else {
const match = backup.id.match(/app_(.+?)_.+/);
if (match) identifier = match[1];
}
db.runSql('UPDATE backups SET identifier=? WHERE id=?', [ identifier, backup.id ], next);
}, function (error) {
if (error) return callback(error);
db.runSql('ALTER TABLE backups MODIFY COLUMN identifier VARCHAR(128) NOT NULL', callback);
});
});
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE backups DROP COLUMN identifier', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,16 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users ADD COLUMN ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP', function (error) {
if (error) console.error(error);
db.runSql('ALTER TABLE users DROP COLUMN modifiedAt', callback);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN ts', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,29 @@
'use strict';
exports.up = function(db, callback) {
db.all('SELECT value FROM settings WHERE name="backup_config"', function (error, results) {
if (error || results.length === 0) return callback(error);
var backupConfig = JSON.parse(results[0].value);
if (backupConfig.intervalSecs === 6 * 60 * 60) { // every 6 hours
backupConfig.schedulePattern = '00 00 5,11,17,23 * * *';
} else if (backupConfig.intervalSecs === 12 * 60 * 60) { // every 12 hours
backupConfig.schedulePattern = '00 00 5,17 * * *';
} else if (backupConfig.intervalSecs === 24 * 60 * 60) { // every day
backupConfig.schedulePattern = '00 00 23 * * *';
} else if (backupConfig.intervalSecs === 3 * 24 * 60 * 60) { // every 3 days (based on day)
backupConfig.schedulePattern = '00 00 23 * * 1,3,5';
} else if (backupConfig.intervalSecs === 7 * 24 * 60 * 60) { // every week (saturday)
backupConfig.schedulePattern = '00 00 23 * * 6';
} else { // default to everyday
backupConfig.schedulePattern = '00 00 23 * * *';
}
delete backupConfig.intervalSecs;
db.runSql('UPDATE settings SET value=? WHERE name="backup_config"', [ JSON.stringify(backupConfig) ], callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,23 @@
'use strict';
const async = require('async');
exports.up = function(db, callback) {
db.all('SELECT value FROM settings WHERE name="admin_domain"', function (error, results) {
if (error || results.length === 0) return callback(error);
const adminDomain = results[0].value;
async.series([
db.runSql.bind(db, 'INSERT INTO settings (name, value) VALUES (?, ?)', [ 'mail_domain', adminDomain ]),
db.runSql.bind(db, 'INSERT INTO settings (name, value) VALUES (?, ?)', [ 'mail_fqdn', `my.${adminDomain}` ])
], callback);
});
};
exports.down = function(db, callback) {
async.series([
db.runSql.bind(db, 'DELETE FROM settings WHERE name="mail_domain"'),
db.runSql.bind(db, 'DELETE FROM settings WHERE name="mail_fqdn"'),
], callback);
};
@@ -0,0 +1,22 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
db.runSql('SELECT * FROM settings WHERE name=?', ['app_autoupdate_pattern'], function (error, results) {
if (error || results.length === 0) return callback(error); // will use defaults from box code
var updatePattern = results[0].value; // use app auto update patter for the box as well
async.series([
db.runSql.bind(db, 'START TRANSACTION;'),
db.runSql.bind(db, 'DELETE FROM settings WHERE name=? OR name=?', ['app_autoupdate_pattern', 'box_autoupdate_pattern']),
db.runSql.bind(db, 'INSERT settings (name, value) VALUES(?, ?)', ['autoupdate_pattern', updatePattern]),
db.runSql.bind(db, 'COMMIT')
], callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,15 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE mail ADD COLUMN bannerJson TEXT', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE mail DROP COLUMN bannerJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,27 @@
'use strict';
const OLD_FIREWALL_CONFIG_JSON = '/home/yellowtent/boxdata/firewall-config.json';
const PORTS_FILE = '/home/yellowtent/boxdata/firewall/ports.json';
const BLOCKLIST_FILE = '/home/yellowtent/boxdata/firewall/blocklist.txt';
const fs = require('fs');
exports.up = function (db, callback) {
if (!fs.existsSync(OLD_FIREWALL_CONFIG_JSON)) return callback();
try {
const dataJson = fs.readFileSync(OLD_FIREWALL_CONFIG_JSON, 'utf8');
const data = JSON.parse(dataJson);
fs.writeFileSync(BLOCKLIST_FILE, data.blocklist.join('\n') + '\n', 'utf8');
fs.writeFileSync(PORTS_FILE, JSON.stringify({ allowed_tcp_ports: data.allowed_tcp_ports }, null, 4), 'utf8');
fs.unlinkSync(OLD_FIREWALL_CONFIG_JSON);
} catch (error) {
console.log('Error migrating old firewall config', error);
}
callback();
};
exports.down = function (db, callback) {
callback();
};
@@ -0,0 +1,40 @@
'use strict';
exports.up = function(db, callback) {
var cmd1 = 'CREATE TABLE volumes(' +
'id VARCHAR(128) NOT NULL UNIQUE,' +
'name VARCHAR(256) NOT NULL UNIQUE,' +
'hostPath VARCHAR(1024) NOT NULL UNIQUE,' +
'creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,' +
'PRIMARY KEY (id)) CHARACTER SET utf8 COLLATE utf8_bin';
var cmd2 = 'CREATE TABLE appMounts(' +
'appId VARCHAR(128) NOT NULL,' +
'volumeId VARCHAR(128) NOT NULL,' +
'readOnly BOOLEAN DEFAULT 1,' +
'UNIQUE KEY appMounts_appId_volumeId (appId, volumeId),' +
'FOREIGN KEY(appId) REFERENCES apps(id),' +
'FOREIGN KEY(volumeId) REFERENCES volumes(id)) CHARACTER SET utf8 COLLATE utf8_bin;';
db.runSql(cmd1, function (error) {
if (error) console.error(error);
db.runSql(cmd2, function (error) {
if (error) console.error(error);
db.runSql('ALTER TABLE apps DROP COLUMN bindsJson', callback);
});
});
};
exports.down = function(db, callback) {
db.runSql('DROP TABLE appMounts', function (error) {
if (error) console.error(error);
db.runSql('DROP TABLE volumes', function (error) {
if (error) console.error(error);
callback(error);
});
});
};
@@ -0,0 +1,16 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN proxyAuth BOOLEAN DEFAULT 0', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN proxyAuth', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,18 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE mailboxes ADD COLUMN ownerType VARCHAR(16)'),
db.runSql.bind(db, 'UPDATE mailboxes SET ownerType=?', [ 'user' ]),
db.runSql.bind(db, 'ALTER TABLE mailboxes MODIFY ownerType VARCHAR(16) NOT NULL'),
], callback);
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE mailboxes DROP COLUMN ownerType', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,13 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE apps DROP COLUMN httpPort')
], callback);
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,29 @@
'use strict';
const async = require('async'),
iputils = require('../src/iputils.js');
exports.up = function(db, callback) {
db.runSql('ALTER TABLE apps ADD COLUMN containerIp VARCHAR(16) UNIQUE', function (error) {
if (error) console.error(error);
let baseIp = iputils.intFromIp('172.18.16.0');
db.all('SELECT * FROM apps', function (error, apps) {
if (error) return callback(error);
async.eachSeries(apps, function (app, iteratorDone) {
const nextIp = iputils.ipFromInt(++baseIp);
db.runSql('UPDATE apps SET containerIp=? WHERE id=?', [ nextIp, app.id ], iteratorDone);
}, callback);
});
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE apps DROP COLUMN containerIp', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,21 @@
'use strict';
exports.up = function(db, callback) {
db.all('SELECT * FROM settings WHERE name=?', ['platform_config'], function (error, results) {
let value;
if (error || results.length === 0) {
value = { sftp: { requireAdmin: true } };
} else {
value = JSON.parse(results[0].value);
if (!value.sftp) value.sftp = {};
value.sftp.requireAdmin = true;
}
// existing installations may not even have the key. so use REPLACE instead of UPDATE
db.runSql('REPLACE INTO settings (name, value) VALUES (?, ?)', [ 'platform_config', JSON.stringify(value) ], callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,18 @@
'use strict';
var async = require('async');
exports.up = function(db, callback) {
async.series([
db.runSql.bind(db, 'CREATE TABLE groupMembers_copy(groupId VARCHAR(128) NOT NULL, userId VARCHAR(128) NOT NULL, FOREIGN KEY(groupId) REFERENCES userGroups(id), FOREIGN KEY(userId) REFERENCES users(id), UNIQUE (groupId, userId)) CHARACTER SET utf8 COLLATE utf8_bin'), // In mysql CREATE TABLE.. LIKE does not copy indexes
db.runSql.bind(db, 'INSERT INTO groupMembers_copy SELECT * FROM groupMembers GROUP BY groupId, userId'),
db.runSql.bind(db, 'DROP TABLE groupMembers'),
db.runSql.bind(db, 'ALTER TABLE groupMembers_copy RENAME TO groupMembers')
], callback);
};
exports.down = function(db, callback) {
async.series([
db.runSql.bind(db, 'ALTER TABLE groupMembers DROP INDEX groupMembers_member'),
], callback);
};
@@ -0,0 +1,51 @@
'use strict';
const async = require('async'),
safe = require('safetydance');
exports.up = function(db, callback) {
db.runSql('ALTER TABLE domains ADD COLUMN wellKnownJson TEXT', function (error) {
if (error) return callback(error);
// keep the paths around, so that we don't need to trigger a re-configure. the old nginx config will use the paths
// the new one will proxy calls to the box code
const WELLKNOWN_DIR = '/home/yellowtent/boxdata/well-known';
const output = safe.child_process.execSync('find . -type f -printf "%P\n"', { cwd: WELLKNOWN_DIR, encoding: 'utf8' });
if (!output) return callback();
const paths = output.trim().split('\n');
if (paths.length === 0) return callback(); // user didn't configure any well-known
let wellKnown = {};
for (let path of paths) {
const fqdn = path.split('/', 1)[0];
const loc = path.slice(fqdn.length+1);
const doc = safe.fs.readFileSync(`${WELLKNOWN_DIR}/${path}`, { encoding: 'utf8' });
if (!doc) continue;
wellKnown[fqdn] = {};
wellKnown[fqdn][loc] = doc;
}
console.log('Migrating well-known', JSON.stringify(wellKnown, null, 4));
async.eachSeries(Object.keys(wellKnown), function (fqdn, iteratorDone) {
db.runSql('UPDATE domains SET wellKnownJson=? WHERE domain=?', [ JSON.stringify(wellKnown[fqdn]), fqdn ], function (error, result) {
if (error) {
console.error(error); // maybe the domain does not exist anymore
} else if (result.affectedRows === 0) {
console.log(`Could not migrate wellknown as domain ${fqdn} is missing`);
}
iteratorDone();
});
}, function (error) {
callback(error);
});
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE domains DROP COLUMN wellKnownJson', function (error) {
if (error) console.error(error);
callback(error);
});
};
@@ -0,0 +1,23 @@
'use strict';
exports.up = function(db, callback) {
db.all('SELECT * FROM settings WHERE name=?', ['platform_config'], function (error, results) {
if (error || results.length === 0) return callback(null);
let value = JSON.parse(results[0].value);
for (const serviceName of Object.keys(value)) {
const service = value[serviceName];
if (!service.memorySwap) continue;
service.memoryLimit = service.memorySwap;
delete service.memorySwap;
delete service.memory;
}
db.runSql('UPDATE settings SET value=? WHERE name=?', [ JSON.stringify(value), 'platform_config' ], callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,28 @@
'use strict';
const async = require('async');
exports.up = function(db, callback) {
db.all('SELECT * FROM apps', function (error, apps) {
if (error) return callback(error);
async.eachSeries(apps, function (app, iteratorDone) {
if (!app.servicesConfigJson) return iteratorDone();
let servicesConfig = JSON.parse(app.servicesConfigJson);
for (const serviceName of Object.keys(servicesConfig)) {
const service = servicesConfig[serviceName];
if (!service.memorySwap) continue;
service.memoryLimit = service.memorySwap;
delete service.memorySwap;
delete service.memory;
}
db.runSql('UPDATE apps SET servicesConfigJson=? WHERE id=?', [ JSON.stringify(servicesConfig), app.id ], iteratorDone);
}, callback);
});
};
exports.down = function(db, callback) {
callback();
};
@@ -0,0 +1,9 @@
'use strict';
exports.up = function(db, callback) {
db.runSql('UPDATE settings SET name=? WHERE name=?', [ 'services_config', 'platform_config' ], callback);
};
exports.down = function(db, callback) {
db.runSql('UPDATE settings SET name=? WHERE name=?', [ 'platform_config', 'services_config' ], callback);
};
+67 -43
View File
@@ -21,25 +21,31 @@ CREATE TABLE IF NOT EXISTS users(
password VARCHAR(1024) NOT NULL,
salt VARCHAR(512) NOT NULL,
createdAt VARCHAR(512) NOT NULL,
modifiedAt VARCHAR(512) NOT NULL,
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
displayName VARCHAR(512) DEFAULT "",
fallbackEmail VARCHAR(512) DEFAULT "",
twoFactorAuthenticationSecret VARCHAR(128) DEFAULT "",
twoFactorAuthenticationEnabled BOOLEAN DEFAULT false,
admin BOOLEAN DEFAULT false,
source VARCHAR(128) DEFAULT "",
role VARCHAR(32),
resetToken VARCHAR(128) DEFAULT "",
resetTokenCreationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
active BOOLEAN DEFAULT 1,
PRIMARY KEY(id));
CREATE TABLE IF NOT EXISTS userGroups(
id VARCHAR(128) NOT NULL UNIQUE,
name VARCHAR(254) NOT NULL UNIQUE,
source VARCHAR(128) DEFAULT "",
PRIMARY KEY(id));
CREATE TABLE IF NOT EXISTS groupMembers(
groupId VARCHAR(128) NOT NULL,
userId VARCHAR(128) NOT NULL,
FOREIGN KEY(groupId) REFERENCES userGroups(id),
FOREIGN KEY(userId) REFERENCES users(id));
FOREIGN KEY(userId) REFERENCES users(id),
UNIQUE (groupId, userId));
CREATE TABLE IF NOT EXISTS tokens(
id VARCHAR(128) NOT NULL UNIQUE,
@@ -51,51 +57,39 @@ CREATE TABLE IF NOT EXISTS tokens(
expires BIGINT NOT NULL, // FIXME: make this a timestamp
PRIMARY KEY(accessToken));
CREATE TABLE IF NOT EXISTS clients(
id VARCHAR(128) NOT NULL UNIQUE, // prefixed with cid- to identify token easily in auth routes
appId VARCHAR(128) NOT NULL, // name of the client (for external apps) or id of app (for built-in apps)
type VARCHAR(16) NOT NULL,
clientSecret VARCHAR(512) NOT NULL,
redirectURI VARCHAR(512) NOT NULL,
scope VARCHAR(512) NOT NULL,
PRIMARY KEY(id));
CREATE TABLE IF NOT EXISTS apps(
id VARCHAR(128) NOT NULL UNIQUE,
appStoreId VARCHAR(128) NOT NULL,
installationState VARCHAR(512) NOT NULL,
installationProgress TEXT,
runState VARCHAR(512),
appStoreId VARCHAR(128) NOT NULL, // empty for custom apps
installationState VARCHAR(512) NOT NULL, // the active task on the app
runState VARCHAR(512) NOT NULL, // if the app is stopped
health VARCHAR(128),
healthTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, // when the app last responded
containerId VARCHAR(128),
manifestJson TEXT,
httpPort INTEGER, // this is the nginx proxy port and not manifest.httpPort
location VARCHAR(128) NOT NULL,
domain VARCHAR(128) NOT NULL,
accessRestrictionJson TEXT, // { users: [ ], groups: [ ] }
creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, // when the app was installed
updateTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, // when the last app update was done
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, // when this db record was updated (useful for UI caching)
memoryLimit BIGINT DEFAULT 0,
cpuShares INTEGER DEFAULT 512,
xFrameOptions VARCHAR(512),
sso BOOLEAN DEFAULT 1, // whether user chose to enable SSO
debugModeJson TEXT, // options for development mode
robotsTxt TEXT,
reverseProxyConfigJson TEXT, // { robotsTxt, csp }
enableBackup BOOLEAN DEFAULT 1, // misnomer: controls automatic daily backups
enableAutomaticUpdate BOOLEAN DEFAULT 1,
mailboxName VARCHAR(128), // mailbox of this app. default allocated as '.app'
mailboxName VARCHAR(128), // mailbox of this app
mailboxDomain VARCHAR(128), // mailbox domain of this apps
label VARCHAR(128), // display name
tagsJson VARCHAR(2048), // array of tags
dataDir VARCHAR(256) UNIQUE,
taskId INTEGER, // current task
errorJson TEXT,
servicesConfigJson TEXT, // app services configuration
containerIp VARCHAR(16) UNIQUE, // this is not-null because of ip allocation fails, user can 'repair'
// the following fields do not belong here, they can be removed when we use a queue for apptask
restoreConfigJson VARCHAR(256), // used to pass backupId to restore from to apptask
oldConfigJson TEXT, // used to pass old config to apptask (configure, restore)
updateConfigJson TEXT, // used to pass new config to apptask (update)
ownerId VARCHAR(128),
FOREIGN KEY(ownerId) REFERENCES users(id),
FOREIGN KEY(mailboxDomain) REFERENCES domains(domain),
FOREIGN KEY(taskId) REFERENCES tasks(id),
PRIMARY KEY(id));
CREATE TABLE IF NOT EXISTS appPortBindings(
@@ -106,13 +100,6 @@ CREATE TABLE IF NOT EXISTS appPortBindings(
FOREIGN KEY(appId) REFERENCES apps(id),
PRIMARY KEY(hostPort));
CREATE TABLE IF NOT EXISTS authcodes(
authCode VARCHAR(128) NOT NULL UNIQUE,
userId VARCHAR(128) NOT NULL,
clientId VARCHAR(128) NOT NULL,
expiresAt BIGINT NOT NULL, // ## FIXME: make this a timestamp
PRIMARY KEY(authCode));
CREATE TABLE IF NOT EXISTS settings(
name VARCHAR(128) NOT NULL UNIQUE,
value TEXT,
@@ -134,8 +121,10 @@ CREATE TABLE IF NOT EXISTS appEnvVars(
CREATE TABLE IF NOT EXISTS backups(
id VARCHAR(128) NOT NULL,
creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
version VARCHAR(128) NOT NULL, /* app version or box version */
packageVersion VARCHAR(128) NOT NULL, /* app version or box version */
encryptionVersion INTEGER, /* when null, unencrypted backup */
type VARCHAR(16) NOT NULL, /* 'box' or 'app' */
identifier VARCHAR(128) NOT NULL, /* 'box' or the app id */
dependsOn TEXT, /* comma separate list of objects this backup depends on */
state VARCHAR(16) NOT NULL,
manifestJson TEXT, /* to validate if the app can be installed in this version of box */
@@ -159,7 +148,7 @@ CREATE TABLE IF NOT EXISTS domains(
provider VARCHAR(16) NOT NULL,
configJson TEXT, /* JSON containing the dns backend provider config */
tlsConfigJson TEXT, /* JSON containing the tls provider config */
locked BOOLEAN,
wellKnownJson TEXT, /* JSON containing well known docs for this domain */
PRIMARY KEY (domain))
@@ -173,6 +162,9 @@ CREATE TABLE IF NOT EXISTS mail(
mailFromValidation BOOLEAN DEFAULT 1,
catchAllJson TEXT,
relayJson TEXT,
bannerJson TEXT,
dkimSelector VARCHAR(128) NOT NULL DEFAULT "cloudron",
FOREIGN KEY(domain) REFERENCES domains(domain),
PRIMARY KEY(domain))
@@ -190,19 +182,23 @@ CREATE TABLE IF NOT EXISTS mailboxes(
name VARCHAR(128) NOT NULL,
type VARCHAR(16) NOT NULL, /* 'mailbox', 'alias', 'list' */
ownerId VARCHAR(128) NOT NULL, /* user id */
aliasTarget VARCHAR(128), /* the target name type is an alias */
membersJson TEXT, /* members of a group */
ownerType VARCHAR(16) NOT NULL,
aliasName VARCHAR(128), /* the target name type is an alias */
aliasDomain VARCHAR(128), /* the target domain */
membersJson TEXT, /* members of a group. fully qualified */
membersOnly BOOLEAN DEFAULT false,
creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
domain VARCHAR(128),
FOREIGN KEY(domain) REFERENCES mail(domain),
FOREIGN KEY(aliasDomain) REFERENCES mail(domain),
UNIQUE (name, domain));
CREATE TABLE IF NOT EXISTS subdomains(
appId VARCHAR(128) NOT NULL,
domain VARCHAR(128) NOT NULL,
subdomain VARCHAR(128) NOT NULL,
type VARCHAR(128) NOT NULL,
type VARCHAR(128) NOT NULL, /* primary or redirect */
FOREIGN KEY(domain) REFERENCES domains(domain),
FOREIGN KEY(appId) REFERENCES apps(id),
@@ -213,8 +209,8 @@ CREATE TABLE IF NOT EXISTS tasks(
type VARCHAR(32) NOT NULL,
percent INTEGER DEFAULT 0,
message TEXT,
errorMessage TEXT,
result TEXT,
errorJson TEXT,
resultJson TEXT,
creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id));
@@ -227,8 +223,36 @@ CREATE TABLE IF NOT EXISTS notifications(
message TEXT,
acknowledged BOOLEAN DEFAULT false,
creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY appPasswords_name_appId_identifier (name, userId, identifier),
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS appPasswords(
id VARCHAR(128) NOT NULL UNIQUE,
name VARCHAR(128) NOT NULL,
userId VARCHAR(128) NOT NULL,
identifier VARCHAR(128) NOT NULL, // resourceId: app id or mail or webadmin
hashedPassword VARCHAR(1024) NOT NULL,
creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(userId) REFERENCES users(id),
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS volumes(
id VARCHAR(128) NOT NULL UNIQUE,
name VARCHAR(256) NOT NULL UNIQUE,
hostPath VARCHAR(1024) NOT NULL UNIQUE,
creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS appMounts(
appId VARCHAR(128) NOT NULL,
volumeId VARCHAR(128) NOT NULL,
readOnly BOOLEAN DEFAULT 1,
UNIQUE KEY appMounts_appId_volumeId (appId, volumeId),
FOREIGN KEY(appId) REFERENCES apps(id),
FOREIGN KEY(volumeId) REFERENCES volumes(id));
CHARACTER SET utf8 COLLATE utf8_bin;
+1808 -1942
View File
File diff suppressed because it is too large Load Diff
+49 -54
View File
@@ -14,82 +14,77 @@
"node": ">=4.0.0 <=4.1.1"
},
"dependencies": {
"@google-cloud/dns": "^0.9.2",
"@google-cloud/dns": "^1.2.9",
"@google-cloud/storage": "^2.5.0",
"@sindresorhus/df": "^3.1.0",
"async": "^2.6.2",
"aws-sdk": "^2.441.0",
"body-parser": "^1.18.3",
"cloudron-manifestformat": "^2.14.2",
"connect": "^3.6.6",
"connect-ensure-login": "^0.1.1",
"connect-lastmile": "^1.0.2",
"@sindresorhus/df": "git+https://github.com/cloudron-io/df.git#type",
"async": "^2.6.3",
"aws-sdk": "^2.828.0",
"basic-auth": "^2.0.1",
"body-parser": "^1.19.0",
"cloudron-manifestformat": "^5.10.1",
"connect": "^3.7.0",
"connect-lastmile": "^2.0.0",
"connect-timeout": "^1.9.0",
"cookie-parser": "^1.4.4",
"cookie-session": "^1.3.3",
"cron": "^1.7.0",
"csurf": "^1.9.0",
"db-migrate": "^0.11.5",
"db-migrate-mysql": "^1.1.10",
"debug": "^4.1.1",
"cookie-parser": "^1.4.5",
"cookie-session": "^1.4.0",
"cron": "^1.8.2",
"db-migrate": "^0.11.12",
"db-migrate-mysql": "^2.1.2",
"debug": "^4.3.1",
"dockerode": "^2.5.8",
"ejs": "^2.6.1",
"ejs-cli": "^2.0.1",
"express": "^4.16.4",
"express-session": "^1.16.1",
"js-yaml": "^3.13.1",
"ejs-cli": "^2.2.1",
"express": "^4.17.1",
"ipaddr.js": "^2.0.0",
"js-yaml": "^3.14.0",
"json": "^9.0.6",
"ldapjs": "^1.0.2",
"jsonwebtoken": "^8.5.1",
"ldapjs": "^2.2.3",
"lodash": "^4.17.20",
"lodash.chunk": "^4.2.0",
"mime": "^2.4.2",
"moment-timezone": "^0.5.25",
"morgan": "^1.9.1",
"multiparty": "^4.2.1",
"mysql": "^2.17.1",
"namecheap": "github:joshuakarjala/node-namecheap#464a952",
"nodemailer": "^6.1.1",
"mime": "^2.5.0",
"moment": "^2.29.1",
"moment-timezone": "^0.5.32",
"morgan": "^1.10.0",
"multiparty": "^4.2.2",
"mustache-express": "^1.3.0",
"mysql": "^2.18.1",
"nodemailer": "^6.4.17",
"nodemailer-smtp-transport": "^2.7.4",
"oauth2orize": "^1.11.0",
"once": "^1.4.0",
"parse-links": "^0.1.0",
"passport": "^0.4.0",
"passport-http": "^0.3.0",
"passport-http-bearer": "^1.0.1",
"passport-local": "^1.0.0",
"passport-oauth2-client-password": "^0.1.2",
"pretty-bytes": "^5.5.0",
"progress-stream": "^2.0.0",
"proxy-middleware": "^0.15.0",
"qrcode": "^1.3.3",
"readdirp": "^3.0.0",
"request": "^2.88.0",
"qrcode": "^1.4.4",
"readdirp": "^3.5.0",
"request": "^2.88.2",
"rimraf": "^2.6.3",
"s3-block-read-stream": "^0.5.0",
"safetydance": "^0.7.1",
"semver": "^6.0.0",
"showdown": "^1.9.0",
"safetydance": "^1.1.1",
"semver": "^6.1.1",
"showdown": "^1.9.1",
"speakeasy": "^2.0.0",
"split": "^1.0.1",
"superagent": "^5.0.2",
"superagent": "^5.3.1",
"supererror": "^0.7.2",
"tar-fs": "github:cloudron-io/tar-fs#ignore_stat_error",
"tar-stream": "^2.0.1",
"tar-stream": "^2.2.0",
"tldjs": "^2.3.1",
"underscore": "^1.9.1",
"uuid": "^3.3.2",
"valid-url": "^1.0.9",
"validator": "^10.11.0",
"ws": "^6.2.1"
"underscore": "^1.12.0",
"uuid": "^3.4.0",
"validator": "^11.0.0",
"ws": "^7.4.2",
"xml2js": "^0.4.23"
},
"devDependencies": {
"expect.js": "*",
"hock": "^1.3.3",
"js2xmlparser": "^4.0.0",
"mocha": "^6.1.4",
"hock": "^1.4.1",
"js2xmlparser": "^4.0.1",
"mocha": "^6.2.3",
"mock-aws-s3": "git+https://github.com/cloudron-io/mock-aws-s3.git",
"nock": "^10.0.6",
"node-sass": "^4.11.0",
"recursive-readdir": "^2.2.2",
"sinon": "^7.3.2"
"node-sass": "^4.14.1",
"recursive-readdir": "^2.2.2"
},
"scripts": {
"test": "./runTests",
+14 -6
View File
@@ -2,11 +2,11 @@
set -eu
readonly SOURCE_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly source_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly DATA_DIR="${HOME}/.cloudron_test"
readonly DEFAULT_TESTS="./src/test/*-test.js ./src/routes/test/*-test.js"
! "${SOURCE_dir}/src/test/checkInstall" && exit 1
! "${source_dir}/src/test/checkInstall" && exit 1
# cleanup old data dirs some of those docker container data requires sudo to be removed
echo "=> Provide root password to purge any leftover data in ${DATA_DIR} and load apparmor profile:"
@@ -22,19 +22,27 @@ fi
mkdir -p ${DATA_DIR}
cd ${DATA_DIR}
mkdir -p appsdata
mkdir -p boxdata/appicons boxdata/mail boxdata/certs boxdata/mail/dkim/localhost boxdata/mail/dkim/foobar.com
mkdir -p platformdata/addons/mail platformdata/nginx/cert platformdata/nginx/applications platformdata/collectd/collectd.conf.d platformdata/addons platformdata/logrotate.d platformdata/backup platformdata/logs/tasks
mkdir -p boxdata/profileicons boxdata/appicons boxdata/mail boxdata/certs boxdata/mail/dkim/localhost boxdata/mail/dkim/foobar.com boxdata/sftp/ssh
mkdir -p platformdata/addons/mail/banner platformdata/nginx/cert platformdata/nginx/applications platformdata/collectd/collectd.conf.d platformdata/addons platformdata/logrotate.d platformdata/backup platformdata/logs/tasks
sudo mkdir -p /mnt/cloudron-test-music /media/cloudron-test-music # volume test
# translations
mkdir -p box/dashboard/dist/translation
cp -r ${source_dir}/../dashboard/dist/translation/* box/dashboard/dist/translation
# put cert
echo "=> Generating a localhost selfsigned cert"
openssl req -x509 -newkey rsa:2048 -keyout platformdata/nginx/cert/host.key -out platformdata/nginx/cert/host.cert -days 3650 -subj '/CN=localhost' -nodes -config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:*.localhost"))
# generate legacy key format for sftp
ssh-keygen -m PEM -t rsa -f boxdata/sftp/ssh/ssh_host_rsa_key -q -N ""
# clear out any containers
echo "=> Delete all docker containers first"
docker ps -qa | xargs --no-run-if-empty docker rm -f
# create docker network (while the infra code does this, most tests skip infra setup)
docker network create --subnet=172.18.0.0/16 cloudron || true
docker network create --subnet=172.18.0.0/16 --ip-range=172.18.0.0/20 cloudron || true
# create the same mysql server version to test with
OUT=`docker inspect mysql-server` || true
@@ -59,7 +67,7 @@ echo "=> Ensure database"
mysql -h"${MYSQL_IP}" -uroot -ppassword -e 'CREATE DATABASE IF NOT EXISTS box'
echo "=> Run database migrations"
cd "${SOURCE_dir}"
cd "${source_dir}"
BOX_ENV=test DATABASE_URL=mysql://root:password@${MYSQL_IP}/box node_modules/.bin/db-migrate up
echo "=> Run tests with mocha"
-106
View File
@@ -1,106 +0,0 @@
#!/bin/bash
set -eu -o pipefail
readonly curl="curl --fail --connect-timeout 20 --retry 10 --retry-delay 2 --max-time 2400 --http1.1"
ip=""
dns_config=""
tls_cert_file=""
tls_key_file=""
license_file=""
backup_config=""
args=$(getopt -o "" -l "ip:,backup-config:,license:,dns-config:,tls-cert:,tls-key:" -n "$0" -- "$@")
eval set -- "${args}"
while true; do
case "$1" in
--ip) ip="$2"; shift 2;;
--dns-config) dns_config="$2"; shift 2;;
--tls-cert) tls_cert_file="$2"; shift 2;;
--tls-key) tls_key_file="$2"; shift 2;;
--license) license_file="$2"; shift 2;;
--backup-config) backup_config="$2"; shift 2;;
--) break;;
*) echo "Unknown option $1"; exit 1;;
esac
done
# validate arguments in the absence of data
if [[ -z "${ip}" ]]; then
echo "--ip is required"
exit 1
fi
if [[ -z "${dns_config}" ]]; then
echo "--dns-config is required"
exit 1
fi
if [[ ! -f "${license_file}" ]]; then
echo "--license must be a valid license file"
exit 1
fi
function get_status() {
key="$1"
if status=$($curl -q -f -k "https://${ip}/api/v1/cloudron/status" 2>/dev/null); then
currentValue=$(echo "${status}" | python3 -c 'import sys, json; print(json.dumps(json.load(sys.stdin)[sys.argv[1]]))' "${key}")
echo "${currentValue}"
return 0
fi
return 1
}
function wait_for_status() {
key="$1"
expectedValue="$2"
echo "wait_for_status: $key to be $expectedValue"
while true; do
if currentValue=$(get_status "${key}"); then
echo "wait_for_status: $key is current: $currentValue expecting: $expectedValue"
if [[ "${currentValue}" == $expectedValue ]]; then
break
fi
fi
sleep 3
done
}
echo "=> Waiting for cloudron to be ready"
wait_for_status "version" '*'
domain=$(echo "${dns_config}" | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj["domain"])')
echo "Provisioning Cloudron ${domain}"
if [[ -n "${tls_cert_file}" && -n "${tls_key_file}" ]]; then
tls_cert=$(cat "${tls_cert_file}" | awk '{printf "%s\\n", $0}')
tls_key=$(cat "${tls_key_file}" | awk '{printf "%s\\n", $0}')
fallback_cert=$(printf '{ "cert": "%s", "key": "%s", "provider": "fallback", "restricted": true }' "${tls_cert}" "${tls_key}")
else
fallback_cert=None
fi
tls_config='{ "provider": "fallback" }'
dns_config=$(echo "${dns_config}" | python3 -c "import json,sys;obj=json.load(sys.stdin);obj.update(tlsConfig=${tls_config});obj.update(fallbackCertficate=${fallback_cert});print(json.dumps(obj))")
license=$(cat "${license_file}")
if [[ -z "${backup_config:-}" ]]; then
backup_config='{ "provider": "filesystem", "backupFolder": "/var/backups", "format": "tgz" }'
fi
setupData=$(printf '{ "dnsConfig": %s, "autoconf": { "appstoreConfig": %s, "backupConfig": %s } }' "${dns_config}" "${license}" "${backup_config}")
if ! setupResult=$($curl -kq -X POST -H "Content-Type: application/json" -d "${setupData}" https://${ip}/api/v1/cloudron/setup); then
echo "Failed to setup with ${setupData} ${setupResult}"
exit 1
fi
wait_for_status "webadminStatus" '*"tls": true*'
echo "Cloudron is ready at https://my-${domain}"
+32 -70
View File
@@ -41,35 +41,39 @@ if systemctl -q is-active box; then
fi
initBaseImage="true"
# provisioning data
provider=""
provider="generic"
requestedVersion=""
installServerOrigin="https://api.cloudron.io"
apiServerOrigin="https://api.cloudron.io"
webServerOrigin="https://cloudron.io"
sourceTarballUrl=""
rebootServer="true"
license=""
setupToken=""
args=$(getopt -o "" -l "help,skip-baseimage-init,provider:,version:,env:,skip-reboot,license:" -n "$0" -- "$@")
args=$(getopt -o "" -l "help,skip-baseimage-init,provider:,version:,env:,skip-reboot,generate-setup-token" -n "$0" -- "$@")
eval set -- "${args}"
while true; do
case "$1" in
--help) echo "See https://cloudron.io/documentation/installation/ on how to install Cloudron"; exit 0;;
--help) echo "See https://docs.cloudron.io/installation/ on how to install Cloudron"; exit 0;;
--provider) provider="$2"; shift 2;;
--version) requestedVersion="$2"; shift 2;;
--env)
if [[ "$2" == "dev" ]]; then
apiServerOrigin="https://api.dev.cloudron.io"
webServerOrigin="https://dev.cloudron.io"
installServerOrigin="https://api.dev.cloudron.io"
elif [[ "$2" == "staging" ]]; then
apiServerOrigin="https://api.staging.cloudron.io"
webServerOrigin="https://staging.cloudron.io"
installServerOrigin="https://api.staging.cloudron.io"
elif [[ "$2" == "unstable" ]]; then
installServerOrigin="https://api.dev.cloudron.io"
fi
shift 2;;
--license) license="$2"; shift 2;;
--skip-baseimage-init) initBaseImage="false"; shift;;
--skip-reboot) rebootServer="false"; shift;;
--generate-setup-token) setupToken="$(openssl rand -hex 10)"; shift;;
--) break;;
*) echo "Unknown option $1"; exit 1;;
esac
@@ -83,49 +87,14 @@ fi
# Only --help works with mismatched ubuntu
ubuntu_version=$(lsb_release -rs)
if [[ "${ubuntu_version}" != "16.04" && "${ubuntu_version}" != "18.04" ]]; then
echo "Cloudron requires Ubuntu 16.04 or 18.04" > /dev/stderr
if [[ "${ubuntu_version}" != "16.04" && "${ubuntu_version}" != "18.04" && "${ubuntu_version}" != "20.04" ]]; then
echo "Cloudron requires Ubuntu 16.04, 18.04 or 20.04" > /dev/stderr
exit 1
fi
# Can only write after we have confirmed script has root access
echo "Running cloudron-setup with args : $@" > "${LOG_FILE}"
# validate arguments in the absence of data
if [[ -z "${provider}" ]]; then
echo "--provider is required (azure, contabo, digitalocean, ec2, exoscale, galaxygate, gce, hetzner, lightsail, linode, netcup, ovh, rosehosting, scaleway, upcloud, vultr or generic)"
exit 1
elif [[ \
"${provider}" != "ami" && \
"${provider}" != "azure" && \
"${provider}" != "caas" && \
"${provider}" != "cloudscale" && \
"${provider}" != "contabo" && \
"${provider}" != "digitalocean" && \
"${provider}" != "digitalocean-mp" && \
"${provider}" != "ec2" && \
"${provider}" != "exoscale" && \
"${provider}" != "galaxygate" && \
"${provider}" != "digitalocean" && \
"${provider}" != "gce" && \
"${provider}" != "hetzner" && \
"${provider}" != "lightsail" && \
"${provider}" != "linode" && \
"${provider}" != "linode-stackscript" && \
"${provider}" != "netcup" && \
"${provider}" != "netcup-image" && \
"${provider}" != "ovh" && \
"${provider}" != "rosehosting" && \
"${provider}" != "scaleway" && \
"${provider}" != "upcloud" && \
"${provider}" != "upcloud-image" && \
"${provider}" != "vultr" && \
"${provider}" != "generic" \
]]; then
echo "--provider must be one of: azure, cloudscale.ch, contabo, digitalocean, ec2, exoscale, galaxygate, gce, hetzner, lightsail, linode, netcup, ovh, rosehosting, scaleway, upcloud, vultr or generic"
exit 1
fi
echo ""
echo "##############################################"
echo " Cloudron Setup (${requestedVersion:-latest})"
@@ -138,32 +107,20 @@ echo " Join us at https://forum.cloudron.io for any questions."
echo ""
if [[ "${initBaseImage}" == "true" ]]; then
echo "=> Installing software-properties-common"
if ! apt-get install -y software-properties-common &>> "${LOG_FILE}"; then
echo "Could not install software-properties-common (for add-apt-repository below). See ${LOG_FILE}"
exit 1
fi
echo "=> Ensure required apt sources"
if ! add-apt-repository universe &>> "${LOG_FILE}"; then
echo "Could not add required apt sources (for nginx-full). See ${LOG_FILE}"
exit 1
fi
echo "=> Updating apt and installing script dependencies"
if ! apt-get update &>> "${LOG_FILE}"; then
echo "Could not update package repositories. See ${LOG_FILE}"
exit 1
fi
if ! apt-get install curl python3 ubuntu-standard -y &>> "${LOG_FILE}"; then
if ! DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -y install --no-install-recommends curl python3 ubuntu-standard software-properties-common -y &>> "${LOG_FILE}"; then
echo "Could not install setup dependencies (curl). See ${LOG_FILE}"
exit 1
fi
fi
echo "=> Checking version"
if ! releaseJson=$($curl -s "${apiServerOrigin}/api/v1/releases?boxVersion=${requestedVersion}"); then
if ! releaseJson=$($curl -s "${installServerOrigin}/api/v1/releases?boxVersion=${requestedVersion}"); then
echo "Failed to get release information"
exit 1
fi
@@ -189,41 +146,46 @@ fi
if [[ "${initBaseImage}" == "true" ]]; then
echo -n "=> Installing base dependencies and downloading docker images (this takes some time) ..."
if ! /bin/bash "${box_src_tmp_dir}/baseimage/initializeBaseUbuntuImage.sh" "${provider}" "../src" &>> "${LOG_FILE}"; then
# initializeBaseUbuntuImage.sh args (provider, infraversion path) are only to support installation of pre 5.3 Cloudrons
if ! /bin/bash "${box_src_tmp_dir}/baseimage/initializeBaseUbuntuImage.sh" "generic" "../src" &>> "${LOG_FILE}"; then
echo "Init script failed. See ${LOG_FILE} for details"
exit 1
fi
echo ""
fi
# NOTE: this install script only supports 3.x and above
# The provider flag is still used for marketplace images
echo "=> Installing version ${version} (this takes some time) ..."
mkdir -p /etc/cloudron
cat > "/etc/cloudron/cloudron.conf" <<CONF_END
{
"apiServerOrigin": "${apiServerOrigin}",
"webServerOrigin": "${webServerOrigin}",
"provider": "${provider}"
}
CONF_END
[[ -n "${license}" ]] && echo -n "$license" > /etc/cloudron/LICENSE
echo "${provider}" > /etc/cloudron/PROVIDER
[[ ! -z "${setupToken}" ]] && echo "${setupToken}" > /etc/cloudron/SETUP_TOKEN
if ! /bin/bash "${box_src_tmp_dir}/scripts/installer.sh" &>> "${LOG_FILE}"; then
echo "Failed to install cloudron. See ${LOG_FILE} for details"
exit 1
fi
mysql -uroot -ppassword -e "REPLACE INTO box.settings (name, value) VALUES ('api_server_origin', '${apiServerOrigin}');" 2>/dev/null
mysql -uroot -ppassword -e "REPLACE INTO box.settings (name, value) VALUES ('web_server_origin', '${webServerOrigin}');" 2>/dev/null
echo -n "=> Waiting for cloudron to be ready (this takes some time) ..."
while true; do
echo -n "."
if status=$($curl -q -f "http://localhost:3000/api/v1/cloudron/status" 2>/dev/null); then
if status=$($curl -s -f "http://localhost:3000/api/v1/cloudron/status" 2>/dev/null); then
break # we are up and running
fi
sleep 10
done
echo -e "\n\n${GREEN}Visit https://<IP> and accept the self-signed certificate to finish setup.${DONE}\n"
if ! ip=$(curl -s --fail --connect-timeout 2 --max-time 2 https://api.cloudron.io/api/v1/helper/public_ip | sed -n -e 's/.*"ip": "\(.*\)"/\1/p'); then
ip='<IP>'
fi
if [[ -z "${setupToken}" ]]; then
url="https://${ip}"
else
url="https://${ip}/?setupToken=${setupToken}"
fi
echo -e "\n\n${GREEN}After reboot, visit ${url} and accept the self-signed certificate to finish setup.${DONE}\n"
if [[ "${rebootServer}" == "true" ]]; then
systemctl stop box mysql # sometimes mysql ends up having corrupt privilege tables
+57 -32
View File
@@ -1,5 +1,7 @@
#!/bin/bash
set -eu -o pipefail
# This script collects diagnostic information to help debug server related issues
# It also enables SSH access for the cloudron support team
@@ -11,25 +13,38 @@ HELP_MESSAGE="
This script collects diagnostic information to help debug server related issues
Options:
--owner-login Login as owner
--enable-ssh Enable SSH access for the Cloudron support team
--help Show this message
"
# We require root
if [[ ${EUID} -ne 0 ]]; then
echo "This script should be run as root." > /dev/stderr
echo "This script should be run as root. Run with sudo"
exit 1
fi
enableSSH="false"
args=$(getopt -o "" -l "help,enable-ssh" -n "$0" -- "$@")
args=$(getopt -o "" -l "help,enable-ssh,admin-login,owner-login" -n "$0" -- "$@")
eval set -- "${args}"
while true; do
case "$1" in
--help) echo -e "${HELP_MESSAGE}"; exit 0;;
--enable-ssh) enableSSH="true"; shift;;
--admin-login)
# fall through
;&
--owner-login)
admin_username=$(mysql -NB -uroot -ppassword -e "SELECT username FROM box.users WHERE role='owner' AND username IS NOT NULL ORDER BY createdAt LIMIT 1" 2>/dev/null)
admin_password=$(pwgen -1s 12)
ghost_file=/home/yellowtent/platformdata/cloudron_ghost.json
printf '{"%s":"%s"}\n' "${admin_username}" "${admin_password}" > "${ghost_file}"
chown yellowtent:yellowtent "${ghost_file}" && chmod o-r,g-r "${ghost_file}"
echo "Login as ${admin_username} / ${admin_password} . This password may only be used once. ${ghost_file} will be automatically removed after use."
exit 0
;;
--) break;;
*) echo "Unknown option $1"; exit 1;;
esac
@@ -42,7 +57,7 @@ if [[ "`df --output="avail" / | sed -n 2p`" -lt "10240" ]]; then
echo ""
df -h
echo ""
echo "To recover from a full disk, follow the guide at https://cloudron.io/documentation/server/#recovery-after-disk-full"
echo "To recover from a full disk, follow the guide at https://docs.cloudron.io/troubleshooting/#recovery-after-disk-full"
exit 1
fi
@@ -58,23 +73,8 @@ echo -n "Generating Cloudron Support stats..."
# clear file
rm -rf $OUT
ssh_port=$(cat /etc/ssh/sshd_config | grep "Port " | sed -e "s/.*Port //")
if [[ $SUDO_USER == "" ]]; then
ssh_user="root"
ssh_folder="/root/.ssh/"
authorized_key_file="${ssh_folder}/authorized_keys"
else
ssh_user="$SUDO_USER"
ssh_folder="/home/$SUDO_USER/.ssh/"
authorized_key_file="${ssh_folder}/authorized_keys"
fi
echo -e $LINE"SSH"$LINE >> $OUT
echo "Username: ${ssh_user}" >> $OUT
echo "Port: ${ssh_port}" >> $OUT
echo -e $LINE"cloudron.conf"$LINE >> $OUT
cat /etc/cloudron/cloudron.conf &>> $OUT
echo -e $LINE"PROVIDER"$LINE >> $OUT
cat /etc/cloudron/PROVIDER &>> $OUT || true
echo -e $LINE"Docker container"$LINE >> $OUT
if ! timeout --kill-after 10s 15s docker ps -a &>> $OUT 2>&1; then
@@ -85,39 +85,64 @@ echo -e $LINE"Filesystem stats"$LINE >> $OUT
df -h &>> $OUT
echo -e $LINE"Appsdata stats"$LINE >> $OUT
du -hcsL /home/yellowtent/appsdata/* &>> $OUT
du -hcsL /home/yellowtent/appsdata/* &>> $OUT || true
echo -e $LINE"Boxdata stats"$LINE >> $OUT
du -hcsL /home/yellowtent/boxdata/* &>> $OUT
echo -e $LINE"Backup stats (possibly misleading)"$LINE >> $OUT
du -hcsL /var/backups/* &>> $OUT
du -hcsL /var/backups/* &>> $OUT || true
echo -e $LINE"System daemon status"$LINE >> $OUT
systemctl status --lines=100 cloudron.target box mysql unbound cloudron-syslog nginx collectd docker &>> $OUT
systemctl status --lines=100 box mysql unbound cloudron-syslog nginx collectd docker &>> $OUT
echo -e $LINE"Box logs"$LINE >> $OUT
tail -n 100 /home/yellowtent/platformdata/logs/box.log &>> $OUT
echo -e $LINE"Firewall chains"$LINE >> $OUT
ip addr &>> $OUT
echo -e $LINE"Firewall chains"$LINE >> $OUT
iptables -L &>> $OUT
echo "Done"
if [[ "${enableSSH}" == "true" ]]; then
ssh_port=$(cat /etc/ssh/sshd_config | grep "Port " | sed -e "s/.*Port //")
permit_root_login=$(grep -q ^PermitRootLogin.*yes /etc/ssh/sshd_config && echo "yes" || echo "no")
# support.js uses similar logic
if [[ -d /home/ubuntu ]]; then
ssh_user="ubuntu"
keys_file="/home/ubuntu/.ssh/authorized_keys"
else
ssh_user="root"
keys_file="/root/.ssh/authorized_keys"
fi
echo -e $LINE"SSH"$LINE >> $OUT
echo "Username: ${ssh_user}" >> $OUT
echo "Port: ${ssh_port}" >> $OUT
echo "PermitRootLogin: ${permit_root_login}" >> $OUT
echo "Key file: ${keys_file}" >> $OUT
echo -n "Enabling ssh access for the Cloudron support team..."
mkdir -p $(dirname "${keys_file}") # .ssh does not exist sometimes
touch "${keys_file}" # required for concat to work
if ! grep -q "${CLOUDRON_SUPPORT_PUBLIC_KEY}" "${keys_file}"; then
echo -e "\n${CLOUDRON_SUPPORT_PUBLIC_KEY}" >> "${keys_file}"
chmod 600 "${keys_file}"
chown "${ssh_user}" "${keys_file}"
fi
echo "Done"
fi
echo -n "Uploading information..."
# for some reason not using $(cat $OUT) will not contain newlines!?
paste_key=$(curl -X POST ${PASTEBIN}/documents --silent -d "$(cat $OUT)" | python3 -c "import sys, json; print(json.load(sys.stdin)['key'])")
echo "Done"
if [[ "${enableSSH}" == "true" ]]; then
echo -n "Enabling ssh access for the Cloudron support team..."
mkdir -p "${ssh_folder}"
echo -e "\n${CLOUDRON_SUPPORT_PUBLIC_KEY}" >> ${authorized_key_file}
chown -R ${ssh_user} "${ssh_folder}"
chmod 600 "${authorized_key_file}"
echo "Done"
fi
echo ""
echo "Please email the following link to support@cloudron.io"
echo ""
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
set -eu -o pipefail
# This script downloads new translation data from weblate at https://translate.cloudron.io
OUT="/home/yellowtent/box/dashboard/dist/translation"
# We require root
if [[ ${EUID} -ne 0 ]]; then
echo "This script should be run as root. Run with sudo"
exit 1
fi
echo "=> Downloading new translation files..."
curl https://translate.cloudron.io/download/cloudron/dashboard/?format=zip -o /tmp/lang.zip
echo "=> Unpacking..."
unzip -jo /tmp/lang.zip -d $OUT
chown -R yellowtent:yellowtent $OUT
# unzip put very restrictive permissions
chmod ua+r $OUT/*
echo "=> Cleanup..."
rm /tmp/lang.zip
echo "=> Done"
echo ""
echo "Reload the dashboard to see the new translations"
echo ""
+2 -2
View File
@@ -41,8 +41,8 @@ if ! $(cd "${SOURCE_DIR}/../dashboard" && git diff --exit-code >/dev/null); then
exit 1
fi
if [[ "$(node --version)" != "v10.15.1" ]]; then
echo "This script requires node 10.15.1"
if [[ "$(node --version)" != "v10.18.1" ]]; then
echo "This script requires node 10.18.1"
exit 1
fi
+35 -21
View File
@@ -11,9 +11,8 @@ if [[ ${EUID} -ne 0 ]]; then
exit 1
fi
readonly USER=yellowtent
readonly BOX_SRC_DIR=/home/${USER}/box
readonly BASE_DATA_DIR=/home/${USER}
readonly user=yellowtent
readonly box_src_dir=/home/${user}/box
readonly curl="curl --fail --connect-timeout 20 --retry 10 --retry-delay 2 --max-time 2400"
readonly script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -24,13 +23,15 @@ readonly ubuntu_codename=$(lsb_release -cs)
readonly is_update=$(systemctl is-active box && echo "yes" || echo "no")
echo "==> installer: Updating from $(cat $box_src_dir/VERSION) to $(cat $box_src_tmp_dir/VERSION) <=="
echo "==> installer: updating docker"
if [[ $(docker version --format {{.Client.Version}}) != "18.09.2" ]]; then
if [[ $(docker version --format {{.Client.Version}}) != "19.03.12" ]]; then
# there are 3 packages for docker - containerd, CLI and the daemon
$curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/containerd.io_1.2.2-3_amd64.deb" -o /tmp/containerd.deb
$curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce-cli_18.09.2~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker-ce-cli.deb
$curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce_18.09.2~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker.deb
curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/containerd.io_1.2.13-2_amd64.deb" -o /tmp/containerd.deb
curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce-cli_19.03.12~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker-ce-cli.deb
curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce_19.03.12~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker.deb
echo "==> installer: Waiting for all dpkg tasks to finish..."
while fuser /var/lib/dpkg/lock; do
@@ -56,13 +57,26 @@ if [[ $(docker version --format {{.Client.Version}}) != "18.09.2" ]]; then
rm /tmp/containerd.deb /tmp/docker-ce-cli.deb /tmp/docker.deb
fi
readonly nginx_version=$(nginx -v 2>&1)
if [[ "${nginx_version}" != *"1.18."* ]]; then
echo "==> installer: installing nginx 1.18"
curl -sL http://nginx.org/packages/ubuntu/pool/nginx/n/nginx/nginx_1.18.0-2~${ubuntu_codename}_amd64.deb -o /tmp/nginx.deb
# apt install with install deps (as opposed to dpkg -i)
apt install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" --force-yes /tmp/nginx.deb
rm /tmp/nginx.deb
fi
# Cloudron 6 on ubuntu 20 installed recommended packages of collectd -> libinotify -> gnome->shell
apt remove -y gnome-shell || true
apt -y autoremove || true
echo "==> installer: updating node"
if [[ "$(node --version)" != "v10.15.1" ]]; then
mkdir -p /usr/local/node-10.15.1
$curl -sL https://nodejs.org/dist/v10.15.1/node-v10.15.1-linux-x64.tar.gz | tar zxvf - --strip-components=1 -C /usr/local/node-10.15.1
ln -sf /usr/local/node-10.15.1/bin/node /usr/bin/node
ln -sf /usr/local/node-10.15.1/bin/npm /usr/bin/npm
rm -rf /usr/local/node-8.11.2 /usr/local/node-8.9.3
if [[ "$(node --version)" != "v10.18.1" ]]; then
mkdir -p /usr/local/node-10.18.1
$curl -sL https://nodejs.org/dist/v10.18.1/node-v10.18.1-linux-x64.tar.gz | tar zxvf - --strip-components=1 -C /usr/local/node-10.18.1
ln -sf /usr/local/node-10.18.1/bin/node /usr/bin/node
ln -sf /usr/local/node-10.18.1/bin/npm /usr/bin/npm
rm -rf /usr/local/node-10.15.1
fi
# this is here (and not in updater.js) because rebuild requires the above node
@@ -109,22 +123,22 @@ while [[ ! -f "${CLOUDRON_SYSLOG}" || "$(${CLOUDRON_SYSLOG} --version)" != ${CLO
sleep 5
done
if ! id "${USER}" 2>/dev/null; then
useradd "${USER}" -m
if ! id "${user}" 2>/dev/null; then
useradd "${user}" -m
fi
if [[ "${is_update}" == "yes" ]]; then
echo "==> installer: stop cloudron.target service for update"
${BOX_SRC_DIR}/setup/stop.sh
echo "==> installer: stop box service for update"
${box_src_dir}/setup/stop.sh
fi
# ensure we are not inside the source directory, which we will remove now
cd /root
echo "==> installer: switching the box code"
rm -rf "${BOX_SRC_DIR}"
mv "${box_src_tmp_dir}" "${BOX_SRC_DIR}"
chown -R "${USER}:${USER}" "${BOX_SRC_DIR}"
rm -rf "${box_src_dir}"
mv "${box_src_tmp_dir}" "${box_src_dir}"
chown -R "${user}:${user}" "${box_src_dir}"
echo "==> installer: calling box setup script"
"${BOX_SRC_DIR}/setup/start.sh"
"${box_src_dir}/setup/start.sh"
+61 -19
View File
@@ -19,6 +19,12 @@ readonly json="$(realpath ${script_dir}/../node_modules/.bin/json)"
readonly ubuntu_version=$(lsb_release -rs)
cp -f "${script_dir}/../scripts/cloudron-support" /usr/bin/cloudron-support
cp -f "${script_dir}/../scripts/cloudron-translation-update" /usr/bin/cloudron-translation-update
# this needs to match the cloudron/base:2.0.0 gid
if ! getent group media; then
addgroup --gid 500 --system media
fi
echo "==> Configuring docker"
cp "${script_dir}/start/docker-cloudron-app.apparmor" /etc/apparmor.d/docker-cloudron-app
@@ -26,7 +32,8 @@ systemctl enable apparmor
systemctl restart apparmor
usermod ${USER} -a -G docker
docker network create --subnet=172.18.0.0/16 cloudron || true
# unbound (which starts after box code) relies on this interface to exist. dockerproxy also relies on this.
docker network create --subnet=172.18.0.0/16 --ip-range=172.18.0.0/20 cloudron || true
mkdir -p "${BOX_DATA_DIR}"
mkdir -p "${APPS_DATA_DIR}"
@@ -39,7 +46,7 @@ mkdir -p "${PLATFORM_DATA_DIR}/mysql"
mkdir -p "${PLATFORM_DATA_DIR}/postgresql"
mkdir -p "${PLATFORM_DATA_DIR}/mongodb"
mkdir -p "${PLATFORM_DATA_DIR}/redis"
mkdir -p "${PLATFORM_DATA_DIR}/addons/mail"
mkdir -p "${PLATFORM_DATA_DIR}/addons/mail/banner"
mkdir -p "${PLATFORM_DATA_DIR}/collectd/collectd.conf.d"
mkdir -p "${PLATFORM_DATA_DIR}/logrotate.d"
mkdir -p "${PLATFORM_DATA_DIR}/acme"
@@ -47,13 +54,18 @@ mkdir -p "${PLATFORM_DATA_DIR}/backup"
mkdir -p "${PLATFORM_DATA_DIR}/logs/backup" \
"${PLATFORM_DATA_DIR}/logs/updater" \
"${PLATFORM_DATA_DIR}/logs/tasks" \
"${PLATFORM_DATA_DIR}/logs/crash"
"${PLATFORM_DATA_DIR}/logs/crash" \
"${PLATFORM_DATA_DIR}/logs/collectd"
mkdir -p "${PLATFORM_DATA_DIR}/update"
mkdir -p "${BOX_DATA_DIR}/appicons"
mkdir -p "${BOX_DATA_DIR}/firewall"
mkdir -p "${BOX_DATA_DIR}/profileicons"
mkdir -p "${BOX_DATA_DIR}/certs"
mkdir -p "${BOX_DATA_DIR}/acme" # acme keys
mkdir -p "${BOX_DATA_DIR}/mail/dkim"
mkdir -p "${BOX_DATA_DIR}/well-known" # .well-known documents
mkdir -p "${BOX_DATA_DIR}/sftp/ssh" # sftp keys
# ensure backups folder exists and is writeable
mkdir -p /var/backups
@@ -77,13 +89,16 @@ systemctl daemon-reload
systemctl restart systemd-journald
setfacl -n -m u:${USER}:r /var/log/journal/*/system.journal
# Give user access to nginx logs (uses adm group)
usermod -a -G adm ${USER}
echo "==> Setting up unbound"
# DO uses Google nameservers by default. This causes RBL queries to fail (host 2.0.0.127.zen.spamhaus.org)
# We do not use dnsmasq because it is not a recursive resolver and defaults to the value in the interfaces file (which is Google DNS!)
# We listen on 0.0.0.0 because there is no way control ordering of docker (which creates the 172.18.0.0/16) and unbound
# If IP6 is not enabled, dns queries seem to fail on some hosts. -s returns false if file missing or 0 size
ip6=$([[ -s /proc/net/if_inet6 ]] && echo "yes" || echo "no")
echo -e "server:\n\tinterface: 0.0.0.0\n\tdo-ip6: ${ip6}\n\taccess-control: 127.0.0.1 allow\n\taccess-control: 172.18.0.1/16 allow\n\tcache-max-negative-ttl: 30\n\tcache-max-ttl: 300\n\t#logfile: /var/log/unbound.log\n\t#verbosity: 10" > /etc/unbound/unbound.conf.d/cloudron-network.conf
cp -f "${script_dir}/start/unbound.conf" /etc/unbound/unbound.conf.d/cloudron-network.conf
# update the root anchor after a out-of-disk-space situation (see #269)
unbound-anchor -a /var/lib/unbound/root.key
@@ -91,10 +106,11 @@ echo "==> Adding systemd services"
cp -r "${script_dir}/start/systemd/." /etc/systemd/system/
[[ "${ubuntu_version}" == "16.04" ]] && sed -e 's/MemoryMax/MemoryLimit/g' -i /etc/systemd/system/box.service
systemctl daemon-reload
systemctl enable --now cloudron-syslog
systemctl enable unbound
systemctl enable cloudron-syslog
systemctl enable cloudron.target
systemctl enable box
systemctl enable cloudron-firewall
systemctl enable --now cloudron-disable-thp
# update firewall rules
systemctl restart cloudron-firewall
@@ -108,27 +124,28 @@ systemctl restart unbound
# ensure cloudron-syslog runs
systemctl restart cloudron-syslog
$json -f /etc/cloudron/cloudron.conf -I -e "delete this.edition" # can be removed after 4.0
echo "==> Ensuring custom.yml"
[[ ! -f /etc/cloudron/custom.yml ]] && cp "${script_dir}/start/custom.yml" /etc/cloudron/custom.yml
echo "==> Configuring sudoers"
rm -f /etc/sudoers.d/${USER}
cp "${script_dir}/start/sudoers" /etc/sudoers.d/${USER}
echo "==> Configuring collectd"
rm -rf /etc/collectd
rm -rf /etc/collectd /var/log/collectd.log
ln -sfF "${PLATFORM_DATA_DIR}/collectd" /etc/collectd
cp "${script_dir}/start/collectd/collectd.conf" "${PLATFORM_DATA_DIR}/collectd/collectd.conf"
if [[ "${ubuntu_version}" == "20.04" ]]; then
# https://bugs.launchpad.net/ubuntu/+source/collectd/+bug/1872281
if ! grep -q LD_PRELOAD /etc/default/collectd; then
echo -e "\nLD_PRELOAD=/usr/lib/python3.8/config-3.8-x86_64-linux-gnu/libpython3.8.so" >> /etc/default/collectd
fi
fi
systemctl restart collectd
echo "==> Configuring logrotate"
if ! grep -q "^include ${PLATFORM_DATA_DIR}/logrotate.d" /etc/logrotate.conf; then
echo -e "\ninclude ${PLATFORM_DATA_DIR}/logrotate.d\n" >> /etc/logrotate.conf
fi
rm -f "${PLATFORM_DATA_DIR}/logrotate.d/"*
cp "${script_dir}/start/logrotate/"* "${PLATFORM_DATA_DIR}/logrotate.d/"
rm -f "${PLATFORM_DATA_DIR}/logrotate.d/box-logrotate" "${PLATFORM_DATA_DIR}/logrotate.d/app-logrotate" # remove pre 3.6 config files
# logrotate files have to be owned by root, this is here to fixup existing installations where we were resetting the owner to yellowtent
chown root:root "${PLATFORM_DATA_DIR}/logrotate.d/"
@@ -147,8 +164,15 @@ cp "${script_dir}/start/nginx/mime.types" "${PLATFORM_DATA_DIR}/nginx/mime.types
if ! grep -q "^Restart=" /etc/systemd/system/multi-user.target.wants/nginx.service; then
# default nginx service file does not restart on crash
echo -e "\n[Service]\nRestart=always\n" >> /etc/systemd/system/multi-user.target.wants/nginx.service
systemctl daemon-reload
fi
# worker_rlimit_nofile in nginx config can be max this number
mkdir -p /etc/systemd/system/nginx.service.d
if ! grep -q "^LimitNOFILE=" /etc/systemd/system/nginx.service.d/cloudron.conf; then
echo -e "[Service]\nLimitNOFILE=16384\n" > /etc/systemd/system/nginx.service.d/cloudron.conf
fi
systemctl daemon-reload
systemctl start nginx
# restart mysql to make sure it has latest config
@@ -171,10 +195,22 @@ fi
readonly mysql_root_password="password"
mysqladmin -u root -ppassword password password # reset default root password
if [[ "${ubuntu_version}" == "20.04" ]]; then
# mysql 8 added a new caching_sha2_password scheme which mysqljs does not support
mysql -u root -p${mysql_root_password} -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '${mysql_root_password}';"
fi
mysql -u root -p${mysql_root_password} -e 'CREATE DATABASE IF NOT EXISTS box'
# set HOME explicity, because it's not set when the installer calls it. this is done because
# paths.js uses this env var and some of the migrate code requires box code
echo "==> Migrating data"
(cd "${BOX_SRC_DIR}" && BOX_ENV=cloudron DATABASE_URL=mysql://root:${mysql_root_password}@127.0.0.1/box "${BOX_SRC_DIR}/node_modules/.bin/db-migrate" up)
cd "${BOX_SRC_DIR}"
if ! HOME=${HOME_DIR} BOX_ENV=cloudron DATABASE_URL=mysql://root:${mysql_root_password}@127.0.0.1/box "${BOX_SRC_DIR}/node_modules/.bin/db-migrate" up; then
echo "DB migration failed"
exit 1
fi
rm -f /etc/cloudron/cloudron.conf
if [[ ! -f "${BOX_DATA_DIR}/dhparams.pem" ]]; then
echo "==> Generating dhparams (takes forever)"
@@ -184,9 +220,15 @@ else
cp "${BOX_DATA_DIR}/dhparams.pem" "${PLATFORM_DATA_DIR}/addons/mail/dhparams.pem"
fi
# old installations used to create appdata/<app>/redis which is now part of old backups and prevents restore
echo "==> Cleaning up stale redis directories"
find "${APPS_DATA_DIR}" -maxdepth 2 -type d -name redis -exec rm -rf {} +
if [[ ! -f "${BOX_DATA_DIR}/sftp/ssh/ssh_host_rsa_key" ]]; then
# the key format in Ubuntu 20 changed, so we create keys in legacy format. for older ubuntu, just re-use the host keys
# see https://github.com/proftpd/proftpd/issues/793
if [[ "${ubuntu_version}" == "20.04" ]]; then
ssh-keygen -m PEM -t rsa -f "${BOX_DATA_DIR}/sftp/ssh/ssh_host_rsa_key" -q -N ""
else
cp /etc/ssh/ssh_host_rsa_key* ${BOX_DATA_DIR}/sftp/ssh
fi
fi
echo "==> Changing ownership"
# be careful of what is chown'ed here. subdirs like mysql,redis etc are owned by the containers and will stop working if perms change
@@ -203,7 +245,7 @@ chown "${USER}:${USER}" "${BOX_DATA_DIR}/mail"
chown "${USER}:${USER}" -R "${BOX_DATA_DIR}/mail/dkim" # this is owned by box currently since it generates the keys
echo "==> Starting Cloudron"
systemctl start cloudron.target
systemctl start box
sleep 2 # give systemd sometime to start the processes
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
set -eu
echo "==> Disabling THP"
# https://docs.couchbase.com/server/current/install/thp-disable.html
if [[ -d /sys/kernel/mm/transparent_hugepage ]]; then
echo "never" > /sys/kernel/mm/transparent_hugepage/enabled
echo "never" > /sys/kernel/mm/transparent_hugepage/defrag
else
echo "==> kernel does not have THP"
fi
+29 -10
View File
@@ -6,11 +6,34 @@ echo "==> Setting up firewall"
iptables -t filter -N CLOUDRON || true
iptables -t filter -F CLOUDRON # empty any existing rules
# NOTE: keep these in sync with src/apps.js validatePortBindings
# allow ssh, http, https, ping, dns
iptables -t filter -I CLOUDRON -m state --state RELATED,ESTABLISHED -j ACCEPT
# ssh is allowed alternately on port 202
iptables -A CLOUDRON -p tcp -m tcp -m multiport --dports 22,25,80,202,443,587,993,4190 -j ACCEPT
# first setup any user IP block lists
ipset create cloudron_blocklist hash:net || true
/home/yellowtent/box/src/scripts/setblocklist.sh
iptables -t filter -A CLOUDRON -m set --match-set cloudron_blocklist src -j DROP
# the DOCKER-USER chain is not cleared on docker restart
if ! iptables -t filter -C DOCKER-USER -m set --match-set cloudron_blocklist src -j DROP; then
iptables -t filter -I DOCKER-USER 1 -m set --match-set cloudron_blocklist src -j DROP
fi
# allow related and establisted connections
iptables -t filter -A CLOUDRON -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A CLOUDRON -p tcp -m tcp -m multiport --dports 22,25,80,202,443 -j ACCEPT # 202 is the alternate ssh port
# whitelist any user ports
ports_json="/home/yellowtent/boxdata/firewall/ports.json"
if allowed_tcp_ports=$(node -e "console.log(JSON.parse(fs.readFileSync('${ports_json}', 'utf8')).allowed_tcp_ports.join(','))" 2>/dev/null); then
[[ -n "${allowed_tcp_ports}" ]] && iptables -A CLOUDRON -p tcp -m tcp -m multiport --dports "${allowed_tcp_ports}" -j ACCEPT
fi
if allowed_udp_ports=$(node -e "console.log(JSON.parse(fs.readFileSync('${ports_json}', 'utf8')).allowed_udp_ports.join(','))" 2>/dev/null); then
[[ -n "${allowed_tcp_ports}" ]] && iptables -A CLOUDRON -p udp -m udp -m multiport --dports "${allowed_tcp_ports}" -j ACCEPT
fi
# turn and stun service
iptables -t filter -A CLOUDRON -p tcp -m multiport --dports 3478,5349 -j ACCEPT
iptables -t filter -A CLOUDRON -p udp -m multiport --dports 3478,5349 -j ACCEPT
iptables -t filter -A CLOUDRON -p udp -m multiport --dports 50000:51000 -j ACCEPT
iptables -t filter -A CLOUDRON -p icmp --icmp-type echo-request -j ACCEPT
iptables -t filter -A CLOUDRON -p icmp --icmp-type echo-reply -j ACCEPT
@@ -47,8 +70,6 @@ for port in 22 202; do
iptables -A CLOUDRON_RATELIMIT -p tcp --dport ${port} -m state --state NEW -m recent --update --name "public-${port}" --seconds 10 --hitcount 5 -j CLOUDRON_RATELIMIT_LOG
done
# TODO: move docker platform rules to platform.js so it can be specialized to rate limit only when destination is the mail container
# docker translates (dnat) 25, 587, 993, 4190 in the PREROUTING step
for port in 2525 4190 9993; do
iptables -A CLOUDRON_RATELIMIT -p tcp --syn ! -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m connlimit --connlimit-above 50 -j CLOUDRON_RATELIMIT_LOG
@@ -64,12 +85,10 @@ for port in 3306 5432 6379 27017; do
iptables -A CLOUDRON_RATELIMIT -p tcp --syn -s 172.18.0.0/16 -d 172.18.0.0/16 --dport ${port} -m connlimit --connlimit-above 5000 -j CLOUDRON_RATELIMIT_LOG
done
# For ssh, http, https
if ! iptables -t filter -C INPUT -j CLOUDRON_RATELIMIT 2>/dev/null; then
iptables -t filter -I INPUT 1 -j CLOUDRON_RATELIMIT
fi
# For smtp, imap etc routed via docker/nat
# Workaroud issue where Docker insists on adding itself first in FORWARD table
# Workaround issue where Docker insists on adding itself first in FORWARD table
iptables -D FORWARD -j CLOUDRON_RATELIMIT || true
iptables -I FORWARD 1 -j CLOUDRON_RATELIMIT
+18 -4
View File
@@ -3,12 +3,26 @@
printf "**********************************************************************\n\n"
if [[ -z "$(ls -A /home/yellowtent/boxdata/mail/dkim)" ]]; then
if [[ -f /tmp/.cloudron-motd-cache ]]; then
ip=$(cat /tmp/.cloudron-motd-cache)
elif ! ip=$(curl --fail --connect-timeout 2 --max-time 2 -q https://api.cloudron.io/api/v1/helper/public_ip | sed -n -e 's/.*"ip": "\(.*\)"/\1/p'); then
ip='<IP>'
fi
echo "${ip}" > /tmp/.cloudron-motd-cache
if [[ ! -f /etc/cloudron/SETUP_TOKEN ]]; then
url="https://${ip}"
else
setupToken="$(cat /etc/cloudron/SETUP_TOKEN)"
url="https://${ip}/?setupToken=${setupToken}"
fi
printf "\t\t\tWELCOME TO CLOUDRON\n"
printf "\t\t\t-------------------\n"
printf '\n\e[1;32m%-6s\e[m\n\n' "Visit https://<IP> on your browser and accept the self-signed certificate to finish setup."
printf "Cloudron overview - https://cloudron.io/documentation/ \n"
printf "Cloudron setup - https://cloudron.io/documentation/installation/#setup \n"
printf '\n\e[1;32m%-6s\e[m\n\n' "Visit ${url} on your browser and accept the self-signed certificate to finish setup."
printf "Cloudron overview - https://docs.cloudron.io/ \n"
printf "Cloudron setup - https://docs.cloudron.io/installation/#setup \n"
else
printf "\t\t\tNOTE TO CLOUDRON ADMINS\n"
printf "\t\t\t-----------------------\n"
@@ -16,7 +30,7 @@ else
printf "Cloudron relies on and may break your installation. Ubuntu security updates\n"
printf "are automatically installed on this server every night.\n"
printf "\n"
printf "Read more at https://cloudron.io/documentation/security/#os-updates\n"
printf "Read more at https://docs.cloudron.io/security/#os-updates\n"
fi
printf "\nFor help and more information, visit https://forum.cloudron.io\n\n"
+5
View File
@@ -4,6 +4,11 @@ set -eu -o pipefail
readonly APPS_SWAP_FILE="/apps.swap"
if [[ -f "${APPS_SWAP_FILE}" ]]; then
echo "Swap file already exists at /apps.swap . Skipping"
exit
fi
# all sizes are in mb
readonly physical_memory=$(LC_ALL=C free -m | awk '/Mem:/ { print $2 }')
readonly swap_size=$((${physical_memory} > 4096 ? 4096 : ${physical_memory})) # min(RAM, 4GB) if you change this, fix enoughResourcesAvailable() in client.js
+20 -36
View File
@@ -57,7 +57,7 @@ LoadPlugin logfile
<Plugin logfile>
LogLevel "info"
File "/var/log/collectd.log"
File "/home/yellowtent/platformdata/logs/collectd/collectd.log"
Timestamp true
PrintSeverity false
</Plugin>
@@ -121,7 +121,7 @@ LoadPlugin memory
#LoadPlugin netlink
#LoadPlugin network
#LoadPlugin nfs
LoadPlugin nginx
#LoadPlugin nginx
#LoadPlugin notify_desktop
#LoadPlugin notify_email
#LoadPlugin ntpd
@@ -149,7 +149,7 @@ LoadPlugin nginx
#LoadPlugin statsd
LoadPlugin swap
#LoadPlugin table
LoadPlugin tail
#LoadPlugin tail
#LoadPlugin tail_csv
#LoadPlugin tcpconns
#LoadPlugin teamspeak2
@@ -197,42 +197,11 @@ LoadPlugin write_graphite
IgnoreSelected false
</Plugin>
<Plugin nginx>
URL "http://127.0.0.1/nginx_status"
</Plugin>
<Plugin swap>
ReportByDevice false
ReportBytes true
</Plugin>
<Plugin "tail">
<File "/var/log/nginx/error.log">
Instance "nginx"
<Match>
Regex ".*"
DSType "CounterInc"
Type counter
Instance "errors"
</Match>
</File>
<File "/var/log/nginx/access.log">
Instance "nginx"
<Match>
Regex ".*"
DSType "CounterInc"
Type counter
Instance "requests"
</Match>
<Match>
Regex " \".*\" [0-9]+ [0-9]+ ([0-9]+)"
DSType GaugeAverage
Type delay
Instance "response"
</Match>
</File>
</Plugin>
<Plugin python>
# https://blog.dbrgn.ch/2017/3/10/write-a-collectd-python-plugin/
ModulePath "/home/yellowtent/box/setup/start/collectd/"
@@ -240,8 +209,23 @@ LoadPlugin write_graphite
Interactive false
Import "df"
# <Module df>
# </Module>
Import "du"
<Module du>
<Path>
Instance maildata
Dir "/home/yellowtent/boxdata/mail"
</Path>
<Path>
Instance boxdata
Dir "/home/yellowtent/boxdata"
Exclude "mail"
</Path>
<Path>
Instance platformdata
Dir "/home/yellowtent/platformdata"
</Path>
</Module>
</Plugin>
<Plugin write_graphite>
+2 -1
View File
@@ -6,7 +6,7 @@ disks = []
def init():
global disks
lines = [s.split() for s in subprocess.check_output(["df", "--type=ext4", "--output=source,target,size,used,avail"]).splitlines()]
lines = [s.split() for s in subprocess.check_output(["df", "--type=ext4", "--output=source,target,size,used,avail"]).decode('utf-8').splitlines()]
disks = lines[1:] # strip header
collectd.info('custom df plugin initialized with %s' % disks)
@@ -21,6 +21,7 @@ def read():
except:
continue
# type comes from https://github.com/collectd/collectd/blob/master/src/types.db
val = collectd.Values(type='df_complex', plugin='df', plugin_instance=instance)
free = st.f_bavail * st.f_frsize # bavail is for non-root user. bfree is total
+82
View File
@@ -0,0 +1,82 @@
import collectd,os,subprocess,sys,re,time
# https://www.programcreek.com/python/example/106897/collectd.register_read
PATHS = [] # { name, dir, exclude }
# there is a pattern in carbon/storage-schemas.conf which stores values every 12h for a year
INTERVAL = 60 * 60 * 12 # twice a day. change values in docker-graphite if you change this
def du(pathinfo):
# -B1 makes du print block sizes and not apparent sizes (to match df which also uses block sizes)
cmd = 'timeout 1800 du -DsB1 "{}"'.format(pathinfo['dir'])
if pathinfo['exclude'] != '':
cmd += ' --exclude "{}"'.format(pathinfo['exclude'])
collectd.info('computing size with command: %s' % cmd);
try:
size = subprocess.check_output(cmd, shell=True).split()[0].decode('utf-8')
collectd.info('\tsize of %s is %s (time: %i)' % (pathinfo['dir'], size, int(time.time())))
return size
except Exception as e:
collectd.info('\terror getting the size of %s: %s' % (pathinfo['dir'], str(e)))
return 0
def parseSize(size):
units = {"B": 1, "KB": 10**3, "MB": 10**6, "GB": 10**9, "TB": 10**12}
number, unit, _ = re.split('([a-zA-Z]+)', size.upper())
return int(float(number)*units[unit])
def dockerSize():
# use --format '{{json .}}' to dump the string. '{{if eq .Type "Images"}}{{.Size}}{{end}}' still creates newlines
# https://godoc.org/github.com/docker/go-units#HumanSize is used. so it's 1000 (KB) and not 1024 (KiB)
cmd = 'timeout 1800 docker system df --format "{{.Size}}" | head -n1'
try:
size = subprocess.check_output(cmd, shell=True).strip().decode('utf-8')
collectd.info('size of docker images is %s (%s) (time: %i)' % (size, parseSize(size), int(time.time())))
return parseSize(size)
except Exception as e:
collectd.info('error getting docker images size : %s' % str(e))
return 0
# configure is called for each module block. this is called before init
def configure(config):
global PATHS
for child in config.children:
if child.key != 'Path':
collectd.info('du plugin: Unknown config key "%s"' % key)
continue
pathinfo = { 'name': '', 'dir': '', 'exclude': '' }
for node in child.children:
if node.key == 'Instance':
pathinfo['name'] = node.values[0]
elif node.key == 'Dir':
pathinfo['dir'] = node.values[0]
elif node.key == 'Exclude':
pathinfo['exclude'] = node.values[0]
PATHS.append(pathinfo);
collectd.info('du plugin: monitoring %s' % pathinfo['dir']);
def init():
global PATHS
collectd.info('custom du plugin initialized with %s %s' % (PATHS, sys.version))
def read():
for pathinfo in PATHS:
size = du(pathinfo)
# type comes from https://github.com/collectd/collectd/blob/master/src/types.db
val = collectd.Values(type='capacity', plugin='du', plugin_instance=pathinfo['name'])
val.dispatch(values=[size], type_instance='usage')
size = dockerSize()
val = collectd.Values(type='capacity', plugin='du', plugin_instance='docker')
val.dispatch(values=[size], type_instance='usage')
collectd.register_init(init)
collectd.register_config(configure)
collectd.register_read(read, INTERVAL)
-15
View File
@@ -1,15 +0,0 @@
# add customizations here
# after making changes run "sudo systemctl restart box"
features:
configureBackup: true
dynamicDns: true
subscription: true
remoteSupport: true
support:
email: support@cloudron.io
alerts:
email: support@cloudron.io
notifyCloudronAdmins: false
-18
View File
@@ -1,18 +0,0 @@
# logrotate config for app, crash, addon and task logs
# man 7 glob
/home/yellowtent/platformdata/logs/[!t][!a][!s][!k][!s]/*.log {
# only keep one rotated file, we currently do not send that over the api
rotate 1
size 10M
# we never compress so we can simply tail the files
nocompress
copytruncate
}
/home/yellowtent/platformdata/logs/tasks/*.log {
monthly
rotate 0
missingok
}
+2 -1
View File
@@ -1,7 +1,8 @@
# logrotate config for box logs
# keep upto 5 logs of size 10M each
/home/yellowtent/platformdata/logs/box.log {
rotate 10
rotate 5
size 10M
# we never compress so we can simply tail the files
nocompress
+34
View File
@@ -0,0 +1,34 @@
# logrotate config for app, crash, addon and task logs
# man 7 glob
/home/yellowtent/platformdata/logs/graphite/*.log
/home/yellowtent/platformdata/logs/mail/*.log
/home/yellowtent/platformdata/logs/mysql/*.log
/home/yellowtent/platformdata/logs/mongodb/*.log
/home/yellowtent/platformdata/logs/postgresql/*.log
/home/yellowtent/platformdata/logs/sftp/*.log
/home/yellowtent/platformdata/logs/redis-*/*.log
/home/yellowtent/platformdata/logs/crash/*.log
/home/yellowtent/platformdata/logs/collectd/*.log
/home/yellowtent/platformdata/logs/turn/*.log
/home/yellowtent/platformdata/logs/updater/*.log {
# only keep one rotated file, we currently do not send that over the api
rotate 1
size 10M
missingok
# we never compress so we can simply tail the files
nocompress
# this truncates the original log file and not the rotated one
copytruncate
}
# keep task logs for a week. the 'nocreate' option ensures empty log files are not
# created post rotation
/home/yellowtent/platformdata/logs/tasks/*.log {
minage 7
daily
rotate 0
missingok
nocreate
}
+1 -1
View File
@@ -6,7 +6,7 @@
performance_schema=OFF
max_connections=50
# on ec2, without this we get a sporadic connection drop when doing the initial migration
max_allowed_packet=32M
max_allowed_packet=64M
# https://mathiasbynens.be/notes/mysql-utf8mb4
character-set-server = utf8mb4
+9 -20
View File
@@ -1,11 +1,18 @@
user www-data;
worker_processes 1;
# detect based on available CPU cores
worker_processes auto;
# this is 4096 by default. See /proc/<PID>/limits and /etc/security/limits.conf
# usually twice the worker_connections (one for uptsream, one for downstream)
# see also LimitNOFILE=16384 in systemd drop-in
worker_rlimit_nofile 8192;
pid /run/nginx.pid;
events {
worker_connections 1024;
# a single worker has these many simultaneous connections max
worker_connections 4096;
}
http {
@@ -36,23 +43,5 @@ http {
# zones for rate limiting
limit_req_zone $binary_remote_addr zone=admin_login:10m rate=10r/s; # 10 request a second
# default http server that returns 404 for any domain we are not listening on
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name does_not_match_anything;
# acme challenges (for app installation and re-configure when the vhost config does not exist)
location /.well-known/acme-challenge/ {
default_type text/plain;
alias /home/yellowtent/platformdata/acme/;
}
location / {
return 404;
}
}
include applications/*.conf;
}
+12
View File
@@ -50,3 +50,15 @@ yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/restartdocker.s
Defaults!/home/yellowtent/box/src/scripts/restartunbound.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/restartunbound.sh
Defaults!/home/yellowtent/box/src/scripts/rmmailbox.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/rmmailbox.sh
Defaults!/home/yellowtent/box/src/scripts/starttask.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD:SETENV: /home/yellowtent/box/src/scripts/starttask.sh
Defaults!/home/yellowtent/box/src/scripts/stoptask.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/stoptask.sh
Defaults!/home/yellowtent/box/src/scripts/setblocklist.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/setblocklist.sh
+5 -4
View File
@@ -1,20 +1,21 @@
[Unit]
Description=Cloudron Admin
OnFailure=crashnotifier@%n.service
StopWhenUnneeded=true
; journald crashes result in a EPIPE in node. Cannot ignore it as it results in loss of logs.
BindsTo=systemd-journald.service
After=mysql.service nginx.service
; As cloudron-resize-fs is a one-shot, the Wants= automatically ensures that the service *finishes*
Wants=cloudron-resize-fs.service
[Install]
WantedBy=multi-user.target
[Service]
Type=idle
WorkingDirectory=/home/yellowtent/box
Restart=always
; Systemd does not append logs when logging to files, we spawn a shell first and exec to replace it after setting up the pipes
ExecStart=/bin/sh -c 'echo "Logging to /home/yellowtent/platformdata/logs/box.log"; exec /usr/bin/node --max_old_space_size=150 /home/yellowtent/box/box.js >> /home/yellowtent/platformdata/logs/box.log 2>&1'
Environment="HOME=/home/yellowtent" "USER=yellowtent" "DEBUG=box*,connect-lastmile" "BOX_ENV=cloudron" "NODE_ENV=production"
ExecStart=/home/yellowtent/box/box.js
Environment="HOME=/home/yellowtent" "USER=yellowtent" "DEBUG=box:*,connect-lastmile,-box:ldap" "BOX_ENV=cloudron" "NODE_ENV=production"
; kill apptask processes as well
KillMode=control-group
; Do not kill this process on OOM. Children inherit this score. Do not set it to -1000 so that MemoryMax can keep working
@@ -0,0 +1,15 @@
# https://docs.mongodb.com/manual/tutorial/transparent-huge-pages/
[Unit]
Description=Disable Transparent Huge Pages (THP)
DefaultDependencies=no
After=sysinit.target local-fs.target
Before=docker.service
[Service]
Type=oneshot
ExecStart="/home/yellowtent/box/setup/start/cloudron-disable-thp.sh"
RemainAfterExit=yes
[Install]
WantedBy=basic.target
-10
View File
@@ -1,10 +0,0 @@
[Unit]
Description=Cloudron Smartserver
Documentation=https://cloudron.io/documentation.html
StopWhenUnneeded=true
Requires=box.service
After=box.service
# AllowIsolate=yes
[Install]
WantedBy=multi-user.target
+1 -1
View File
@@ -2,7 +2,7 @@
[Unit]
Description=Unbound DNS Resolver
After=network.target
After=network.target docker.service
[Service]
PIDFile=/run/unbound.pid
+13
View File
@@ -0,0 +1,13 @@
server:
port: 53
interface: 127.0.0.1
interface: 172.18.0.1
do-ip6: no
access-control: 127.0.0.1 allow
access-control: 172.18.0.1/16 allow
cache-max-negative-ttl: 30
cache-max-ttl: 300
# enable below for logging to journalctl -u unbound
# verbosity: 5
# log-queries: yes
+1 -1
View File
@@ -4,4 +4,4 @@ set -eu -o pipefail
echo "Stopping cloudron"
systemctl stop cloudron.target
systemctl stop box
+9 -124
View File
@@ -1,144 +1,29 @@
'use strict';
exports = module.exports = {
SCOPE_APPS_READ: 'apps:read',
SCOPE_APPS_MANAGE: 'apps:manage',
SCOPE_APPSTORE: 'appstore',
SCOPE_CLIENTS: 'clients',
SCOPE_CLOUDRON: 'cloudron',
SCOPE_DOMAINS_READ: 'domains:read',
SCOPE_DOMAINS_MANAGE: 'domains:manage',
SCOPE_MAIL: 'mail',
SCOPE_PROFILE: 'profile',
SCOPE_SETTINGS: 'settings',
SCOPE_SUBSCRIPTION: 'subscription',
SCOPE_USERS_READ: 'users:read',
SCOPE_USERS_MANAGE: 'users:manage',
VALID_SCOPES: [ 'apps', 'appstore', 'clients', 'cloudron', 'domains', 'mail', 'profile', 'settings', 'subscription', 'users' ], // keep this sorted
SCOPE_ANY: '*',
validateScopeString: validateScopeString,
hasScopes: hasScopes,
canonicalScopeString: canonicalScopeString,
intersectScopes: intersectScopes,
validateToken: validateToken,
scopesForUser: scopesForUser
verifyToken: verifyToken
};
var assert = require('assert'),
config = require('./config.js'),
DatabaseError = require('./databaseerror.js'),
debug = require('debug')('box:accesscontrol'),
BoxError = require('./boxerror.js'),
tokendb = require('./tokendb.js'),
users = require('./users.js'),
UsersError = users.UsersError,
_ = require('underscore');
users = require('./users.js');
// returns scopes that does not have wildcards and is sorted
function canonicalScopeString(scope) {
if (scope === exports.SCOPE_ANY) return exports.VALID_SCOPES.join(',');
return scope.split(',').sort().join(',');
}
function intersectScopes(allowedScopes, wantedScopes) {
assert(Array.isArray(allowedScopes), 'Expecting sorted array');
assert(Array.isArray(wantedScopes), 'Expecting sorted array');
if (_.isEqual(allowedScopes, wantedScopes)) return allowedScopes; // quick path
let wantedScopesMap = new Map();
let results = [];
// make a map of scope -> [ subscopes ]
for (let w of wantedScopes) {
let parts = w.split(':');
let subscopes = wantedScopesMap.get(parts[0]) || new Set();
subscopes.add(parts[1] || '*');
wantedScopesMap.set(parts[0], subscopes);
}
for (let a of allowedScopes) {
let parts = a.split(':');
let as = parts[1] || '*';
let subscopes = wantedScopesMap.get(parts[0]);
if (!subscopes) continue;
if (subscopes.has('*') || subscopes.has(as)) {
results.push(a);
} else if (as === '*') {
results = results.concat(Array.from(subscopes).map(function (ss) { return `${a}:${ss}`; }));
}
}
return results;
}
function validateScopeString(scope) {
assert.strictEqual(typeof scope, 'string');
if (scope === '') return new Error('Empty scope not allowed');
// NOTE: this function intentionally does not allow '*'. This is only allowed in the db to allow
// us not write a migration script every time we add a new scope
var allValid = scope.split(',').every(function (s) { return exports.VALID_SCOPES.indexOf(s.split(':')[0]) !== -1; });
if (!allValid) return new Error('Invalid scope. Available scopes are ' + exports.VALID_SCOPES.join(', '));
return null;
}
// tests if all requiredScopes are attached to the request
function hasScopes(authorizedScopes, requiredScopes) {
assert(Array.isArray(authorizedScopes), 'Expecting array');
assert(Array.isArray(requiredScopes), 'Expecting array');
if (authorizedScopes.indexOf(exports.SCOPE_ANY) !== -1) return null;
for (var i = 0; i < requiredScopes.length; ++i) {
const scopeParts = requiredScopes[i].split(':');
// this allows apps:write if the token has a higher apps scope
if (authorizedScopes.indexOf(requiredScopes[i]) === -1 && authorizedScopes.indexOf(scopeParts[0]) === -1) {
debug('scope: missing scope "%s".', requiredScopes[i]);
return new Error('Missing required scope "' + requiredScopes[i] + '"');
}
}
return null;
}
function scopesForUser(user, callback) {
assert.strictEqual(typeof user, 'object');
assert.strictEqual(typeof callback, 'function');
if (user.admin) return callback(null, exports.VALID_SCOPES);
callback(null, [ 'profile', 'apps:read' ]);
}
function validateToken(accessToken, callback) {
function verifyToken(accessToken, callback) {
assert.strictEqual(typeof accessToken, 'string');
assert.strictEqual(typeof callback, 'function');
tokendb.getByAccessToken(accessToken, function (error, token) {
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, null /* user */, 'Invalid Token'); // will end up as a 401
if (error) return callback(error); // this triggers 'internal error' in passport
if (error && error.reason === BoxError.NOT_FOUND) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (error) return callback(error);
users.get(token.identifier, function (error, user) {
if (error && error.reason === UsersError.NOT_FOUND) return callback(null, null /* user */, 'Invalid Token'); // will end up as a 401
if (error && error.reason === BoxError.NOT_FOUND) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (error) return callback(error);
scopesForUser(user, function (error, userScopes) {
if (error) return callback(error);
if (!user.active) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
var authorizedScopes = intersectScopes(userScopes, token.scope.split(','));
const skipPasswordVerification = token.clientId === 'cid-sdk' || token.clientId === 'cid-cli'; // these clients do not require password checks unlike UI
var info = { authorizedScopes: authorizedScopes, skipPasswordVerification: skipPasswordVerification }; // ends up in req.authInfo
callback(null, user, info);
});
callback(null, user);
});
});
}
-1795
View File
File diff suppressed because it is too large Load Diff
-189
View File
@@ -1,189 +0,0 @@
# http://nginx.org/en/docs/http/websocket.html
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# http server
server {
listen 80;
<% if (hasIPv6) { -%>
listen [::]:80;
<% } -%>
<% if (vhost) { -%>
server_name <%= vhost %>;
<% } else { -%>
# IP based access from collectd or initial cloudron setup. TODO: match the IPv6 address
server_name "~^\d+\.\d+\.\d+\.\d+$";
# collectd
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
<% } -%>
# acme challenges (for cert renewal where the vhost config exists)
location /.well-known/acme-challenge/ {
default_type text/plain;
alias /home/yellowtent/platformdata/acme/;
}
location / {
# redirect everything to HTTPS
return 301 https://$host$request_uri;
}
}
# https server
server {
<% if (vhost) { -%>
server_name <%= vhost %>;
listen 443 http2;
<% if (hasIPv6) { -%>
listen [::]:443 http2;
<% } -%>
<% } else { -%>
listen 443 http2 default_server;
<% if (hasIPv6) { -%>
listen [::]:443 http2 default_server;
<% } -%>
<% } -%>
ssl on;
# paths are relative to prefix and not to this file
ssl_certificate <%= certFilePath %>;
ssl_certificate_key <%= keyFilePath %>;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
# https://bettercrypto.org/static/applied-crypto-hardening.pdf
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
# https://cipherli.st/
# https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # don't use SSLv3 ref: POODLE
# ciphers according to https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.10.3&openssl=1.0.2g&hsts=yes&profile=modern
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_dhparam /home/yellowtent/boxdata/dhparams.pem;
add_header Strict-Transport-Security "max-age=15768000";
# https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options
add_header X-Frame-Options "<%= xFrameOptions %>";
proxy_hide_header X-Frame-Options;
# https://github.com/twitter/secureheaders
# https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#tab=Compatibility_Matrix
# https://wiki.mozilla.org/Security/Guidelines/Web_Security
add_header X-XSS-Protection "1; mode=block";
proxy_hide_header X-XSS-Protection;
add_header X-Download-Options "noopen";
proxy_hide_header X-Download-Options;
add_header X-Content-Type-Options "nosniff";
proxy_hide_header X-Content-Type-Options;
add_header X-Permitted-Cross-Domain-Policies "none";
proxy_hide_header X-Permitted-Cross-Domain-Policies;
add_header Referrer-Policy "no-referrer-when-downgrade";
proxy_hide_header Referrer-Policy;
# gzip responses that are > 50k and not images
gzip on;
gzip_min_length 50k;
gzip_types text/css text/javascript text/xml text/plain application/javascript application/x-javascript application/json;
# enable for proxied requests as well
gzip_proxied any;
<% if ( endpoint === 'admin' ) { -%>
# CSP headers for the admin/dashboard resources
add_header Content-Security-Policy "default-src 'none'; connect-src wss: https: 'self' *.cloudron.io; script-src https: 'self' 'unsafe-inline' 'unsafe-eval'; img-src * data:; style-src https: 'unsafe-inline'; object-src 'none'; font-src https: 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self';";
<% } -%>
proxy_http_version 1.1;
proxy_intercept_errors on;
proxy_read_timeout 3500;
proxy_connect_timeout 3250;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Ssl on;
# upgrade is a hop-by-hop header (http://nginx.org/en/docs/http/websocket.html)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# only serve up the status page if we get proxy gateway errors
root <%= sourceDir %>/dashboard/dist;
error_page 502 503 504 /appstatus.html;
location /appstatus.html {
internal;
}
location / {
# increase the proxy buffer sizes to not run into buffer issues (http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers)
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
# No buffering to temp files, it fails for large downloads
proxy_max_temp_file_size 0;
# Disable check to allow unlimited body sizes. this allows apps to accept whatever size they want
client_max_body_size 0;
<% if (robotsTxtQuoted) { %>
location = /robots.txt {
return 200 <%- robotsTxtQuoted %>;
}
<% } %>
<% if ( endpoint === 'admin' ) { %>
location /api/ {
proxy_pass http://127.0.0.1:3000;
client_max_body_size 1m;
}
location ~ ^/api/v1/(developer|session)/login$ {
proxy_pass http://127.0.0.1:3000;
client_max_body_size 1m;
limit_req zone=admin_login burst=5;
}
# the read timeout is between successive reads and not the whole connection
location ~ ^/api/v1/apps/.*/exec$ {
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 30m;
}
location ~ ^/api/v1/apps/.*/upload$ {
proxy_pass http://127.0.0.1:3000;
client_max_body_size 0;
}
# graphite paths (uncomment block below and visit /graphite/index.html)
# remember to comment out the CSP policy as well to access the graphite dashboard
# location ~ ^/(graphite|content|metrics|dashboard|render|browser|composer)/ {
# proxy_pass http://127.0.0.1:8417;
# client_max_body_size 1m;
# }
location / {
root <%= sourceDir %>/dashboard/dist;
index index.html index.htm;
}
<% } else if ( endpoint === 'app' ) { %>
proxy_pass http://127.0.0.1:<%= port %>;
<% } else if ( endpoint === 'redirect' ) { %>
# redirect everything to the app. this is temporary because there is no way
# to clear a permanent redirect on the browser
return 302 https://<%= redirectTo %>$request_uri;
<% } %>
}
}
+173 -282
View File
@@ -1,81 +1,51 @@
'use strict';
exports = module.exports = {
get: get,
getByHttpPort: getByHttpPort,
getByContainerId: getByContainerId,
add: add,
exists: exists,
del: del,
update: update,
getAll: getAll,
getPortBindings: getPortBindings,
delPortBinding: delPortBinding,
get,
add,
exists,
del,
update,
getAll,
getPortBindings,
delPortBinding,
setAddonConfig: setAddonConfig,
getAddonConfig: getAddonConfig,
getAddonConfigByAppId: getAddonConfigByAppId,
getAddonConfigByName: getAddonConfigByName,
unsetAddonConfig: unsetAddonConfig,
unsetAddonConfigByAppId: unsetAddonConfigByAppId,
getAppIdByAddonConfigValue: getAppIdByAddonConfigValue,
setAddonConfig,
getAddonConfig,
getAddonConfigByAppId,
getAddonConfigByName,
unsetAddonConfig,
unsetAddonConfigByAppId,
getAppIdByAddonConfigValue,
getByIpAddress,
setHealth: setHealth,
setInstallationCommand: setInstallationCommand,
setRunCommand: setRunCommand,
getAppStoreIds: getAppStoreIds,
setOwner: setOwner,
transferOwnership: transferOwnership,
// installation codes (keep in sync in UI)
ISTATE_PENDING_INSTALL: 'pending_install', // installs and fresh reinstalls
ISTATE_PENDING_CLONE: 'pending_clone', // clone
ISTATE_PENDING_CONFIGURE: 'pending_configure', // config (location, port) changes and on infra update
ISTATE_PENDING_UNINSTALL: 'pending_uninstall', // uninstallation
ISTATE_PENDING_RESTORE: 'pending_restore', // restore to previous backup or on upgrade
ISTATE_PENDING_UPDATE: 'pending_update', // update from installed state preserving data
ISTATE_PENDING_FORCE_UPDATE: 'pending_force_update', // update from any state preserving data
ISTATE_PENDING_BACKUP: 'pending_backup', // backup the app
ISTATE_ERROR: 'error', // error executing last pending_* command
ISTATE_INSTALLED: 'installed', // app is installed
RSTATE_RUNNING: 'running',
RSTATE_PENDING_START: 'pending_start',
RSTATE_PENDING_STOP: 'pending_stop',
RSTATE_STOPPED: 'stopped', // app stopped by us
// run codes (keep in sync in UI)
HEALTH_HEALTHY: 'healthy',
HEALTH_UNHEALTHY: 'unhealthy',
HEALTH_ERROR: 'error',
HEALTH_DEAD: 'dead',
setHealth,
setTask,
getAppStoreIds,
// subdomain table types
SUBDOMAIN_TYPE_PRIMARY: 'primary',
SUBDOMAIN_TYPE_REDIRECT: 'redirect',
SUBDOMAIN_TYPE_ALIAS: 'alias',
_clear: clear
};
var assert = require('assert'),
async = require('async'),
BoxError = require('./boxerror.js'),
database = require('./database.js'),
DatabaseError = require('./databaseerror'),
safe = require('safetydance'),
util = require('util');
var APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.installationState', 'apps.installationProgress', 'apps.runState',
'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.httpPort', 'subdomains.subdomain AS location', 'subdomains.domain',
'apps.accessRestrictionJson', 'apps.restoreConfigJson', 'apps.oldConfigJson', 'apps.updateConfigJson', 'apps.memoryLimit',
'apps.label', 'apps.tagsJson',
'apps.xFrameOptions', 'apps.sso', 'apps.debugModeJson', 'apps.robotsTxt', 'apps.enableBackup',
'apps.creationTime', 'apps.updateTime', 'apps.ownerId', 'apps.mailboxName', 'apps.enableAutomaticUpdate',
const APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.installationState', 'apps.errorJson', 'apps.runState',
'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.accessRestrictionJson', 'apps.memoryLimit', 'apps.cpuShares',
'apps.label', 'apps.tagsJson', 'apps.taskId', 'apps.reverseProxyConfigJson', 'apps.servicesConfigJson',
'apps.sso', 'apps.debugModeJson', 'apps.enableBackup', 'apps.proxyAuth', 'apps.containerIp',
'apps.creationTime', 'apps.updateTime', 'apps.mailboxName', 'apps.mailboxDomain', 'apps.enableAutomaticUpdate',
'apps.dataDir', 'apps.ts', 'apps.healthTime' ].join(',');
var PORT_BINDINGS_FIELDS = [ 'hostPort', 'type', 'environmentVariable', 'appId' ].join(',');
const SUBDOMAIN_FIELDS = [ 'appId', 'domain', 'subdomain', 'type' ].join(',');
const PORT_BINDINGS_FIELDS = [ 'hostPort', 'type', 'environmentVariable', 'appId' ].join(',');
function postProcess(result) {
assert.strictEqual(typeof result, 'object');
@@ -84,22 +54,14 @@ function postProcess(result) {
result.manifest = safe.JSON.parse(result.manifestJson);
delete result.manifestJson;
assert(result.oldConfigJson === null || typeof result.oldConfigJson === 'string');
result.oldConfig = safe.JSON.parse(result.oldConfigJson);
delete result.oldConfigJson;
assert(result.updateConfigJson === null || typeof result.updateConfigJson === 'string');
result.updateConfig = safe.JSON.parse(result.updateConfigJson);
delete result.updateConfigJson;
assert(result.restoreConfigJson === null || typeof result.restoreConfigJson === 'string');
result.restoreConfig = safe.JSON.parse(result.restoreConfigJson);
delete result.restoreConfigJson;
assert(result.tagsJson === null || typeof result.tagsJson === 'string');
result.tags = safe.JSON.parse(result.tagsJson) || [];
delete result.tagsJson;
assert(result.reverseProxyConfigJson === null || typeof result.reverseProxyConfigJson === 'string');
result.reverseProxyConfig = safe.JSON.parse(result.reverseProxyConfigJson) || {};
delete result.reverseProxyConfigJson;
assert(result.hostPorts === null || typeof result.hostPorts === 'string');
assert(result.environmentVariables === null || typeof result.environmentVariables === 'string');
@@ -121,22 +83,36 @@ function postProcess(result) {
if (result.accessRestriction && !result.accessRestriction.users) result.accessRestriction.users = [];
delete result.accessRestrictionJson;
// TODO remove later once all apps have this attribute
result.xFrameOptions = result.xFrameOptions || 'SAMEORIGIN';
result.sso = !!result.sso; // make it bool
result.enableBackup = !!result.enableBackup; // make it bool
result.enableAutomaticUpdate = !!result.enableAutomaticUpdate; // make it bool
result.proxyAuth = !!result.proxyAuth;
assert(result.debugModeJson === null || typeof result.debugModeJson === 'string');
result.debugMode = safe.JSON.parse(result.debugModeJson);
delete result.debugModeJson;
result.alternateDomains = result.alternateDomains || [];
result.alternateDomains.forEach(function (d) {
delete d.appId;
delete d.type;
});
assert(result.servicesConfigJson === null || typeof result.servicesConfigJson === 'string');
result.servicesConfig = safe.JSON.parse(result.servicesConfigJson) || {};
delete result.servicesConfigJson;
let subdomains = JSON.parse(result.subdomains), domains = JSON.parse(result.domains), subdomainTypes = JSON.parse(result.subdomainTypes);
delete result.subdomains;
delete result.domains;
delete result.subdomainTypes;
result.alternateDomains = [];
result.aliasDomains = [];
for (let i = 0; i < subdomainTypes.length; i++) {
if (subdomainTypes[i] === exports.SUBDOMAIN_TYPE_PRIMARY) {
result.location = subdomains[i];
result.domain = domains[i];
} else if (subdomainTypes[i] === exports.SUBDOMAIN_TYPE_REDIRECT) {
result.alternateDomains.push({ domain: domains[i], subdomain: subdomains[i] });
} else if (subdomainTypes[i] === exports.SUBDOMAIN_TYPE_ALIAS) {
result.aliasDomains.push({ domain: domains[i], subdomain: subdomains[i] });
}
}
let envNames = JSON.parse(result.envNames), envValues = JSON.parse(result.envValues);
delete result.envNames;
@@ -146,128 +122,77 @@ function postProcess(result) {
if (envNames[i]) result.env[envNames[i]] = envValues[i];
}
// in the db, we store dataDir as unique/nullable
result.dataDir = result.dataDir || '';
let volumeIds = JSON.parse(result.volumeIds);
delete result.volumeIds;
let volumeReadOnlys = JSON.parse(result.volumeReadOnlys);
delete result.volumeReadOnlys;
result.mounts = volumeIds[0] === null ? [] : volumeIds.map((v, idx) => { return { volumeId: v, readOnly: !!volumeReadOnlys[idx] }; }); // NOTE: volumeIds is [null] when volumes of an app is empty
result.error = safe.JSON.parse(result.errorJson);
delete result.errorJson;
result.taskId = result.taskId ? String(result.taskId) : null;
}
// each query simply join apps table with another table by id. we then join the full result together
const PB_QUERY = 'SELECT id, GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes FROM apps LEFT JOIN appPortBindings ON apps.id = appPortBindings.appId GROUP BY apps.id';
const ENV_QUERY = 'SELECT id, JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues FROM apps LEFT JOIN appEnvVars ON apps.id = appEnvVars.appId GROUP BY apps.id';
const SUBDOMAIN_QUERY = 'SELECT id, JSON_ARRAYAGG(subdomains.subdomain) AS subdomains, JSON_ARRAYAGG(subdomains.domain) AS domains, JSON_ARRAYAGG(subdomains.type) AS subdomainTypes FROM apps LEFT JOIN subdomains ON apps.id = subdomains.appId GROUP BY apps.id';
const MOUNTS_QUERY = 'SELECT id, JSON_ARRAYAGG(appMounts.volumeId) AS volumeIds, JSON_ARRAYAGG(appMounts.readOnly) AS volumeReadOnlys FROM apps LEFT JOIN appMounts ON apps.id = appMounts.appId GROUP BY apps.id';
const APPS_QUERY = `SELECT ${APPS_FIELDS_PREFIXED}, hostPorts, environmentVariables, portTypes, envNames, envValues, subdomains, domains, subdomainTypes, volumeIds, volumeReadOnlys FROM apps`
+ ` LEFT JOIN (${PB_QUERY}) AS q1 on q1.id = apps.id`
+ ` LEFT JOIN (${ENV_QUERY}) AS q2 on q2.id = apps.id`
+ ` LEFT JOIN (${SUBDOMAIN_QUERY}) AS q3 on q3.id = apps.id`
+ ` LEFT JOIN (${MOUNTS_QUERY}) AS q4 on q4.id = apps.id`;
function get(id, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + APPS_FIELDS_PREFIXED + ','
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes, '
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues'
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
+ ' WHERE apps.id = ? GROUP BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY, id ], function (error, result) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (result.length === 0) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
database.query(`${APPS_QUERY} WHERE apps.id = ?`, [ id ], function (error, result) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
if (result.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
database.query('SELECT ' + SUBDOMAIN_FIELDS + ' FROM subdomains WHERE appId = ? AND type = ?', [ id, exports.SUBDOMAIN_TYPE_REDIRECT ], function (error, alternateDomains) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
postProcess(result[0]);
result[0].alternateDomains = alternateDomains;
postProcess(result[0]);
callback(null, result[0]);
});
callback(null, result[0]);
});
}
function getByHttpPort(httpPort, callback) {
assert.strictEqual(typeof httpPort, 'number');
function getByIpAddress(ip, callback) {
assert.strictEqual(typeof ip, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + APPS_FIELDS_PREFIXED + ','
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,'
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues'
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
+ ' WHERE httpPort = ? GROUP BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY, httpPort ], function (error, result) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (result.length === 0) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
database.query(`${APPS_QUERY} WHERE apps.containerIp = ?`, [ ip ], function (error, result) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
if (result.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
database.query('SELECT ' + SUBDOMAIN_FIELDS + ' FROM subdomains WHERE appId = ? AND type = ?', [ result[0].id, exports.SUBDOMAIN_TYPE_REDIRECT ], function (error, alternateDomains) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
postProcess(result[0]);
result[0].alternateDomains = alternateDomains;
postProcess(result[0]);
callback(null, result[0]);
});
});
}
function getByContainerId(containerId, callback) {
assert.strictEqual(typeof containerId, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + APPS_FIELDS_PREFIXED + ','
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,'
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues'
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
+ ' WHERE containerId = ? GROUP BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY, containerId ], function (error, result) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (result.length === 0) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
database.query('SELECT ' + SUBDOMAIN_FIELDS + ' FROM subdomains WHERE appId = ? AND type = ?', [ result[0].id, exports.SUBDOMAIN_TYPE_REDIRECT ], function (error, alternateDomains) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
result[0].alternateDomains = alternateDomains;
postProcess(result[0]);
callback(null, result[0]);
});
callback(null, result[0]);
});
}
function getAll(callback) {
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + APPS_FIELDS_PREFIXED + ','
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,'
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues'
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
+ ' GROUP BY apps.id ORDER BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY ], function (error, results) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
database.query(`${APPS_QUERY} ORDER BY apps.id`, [ ], function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
database.query('SELECT ' + SUBDOMAIN_FIELDS + ' FROM subdomains WHERE type = ?', [ exports.SUBDOMAIN_TYPE_REDIRECT ], function (error, alternateDomains) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
results.forEach(postProcess);
alternateDomains.forEach(function (d) {
var domain = results.find(function (a) { return d.appId === a.id; });
if (!domain) return;
domain.alternateDomains = domain.alternateDomains || [];
domain.alternateDomains.push(d);
});
results.forEach(postProcess);
callback(null, results);
});
callback(null, results);
});
}
function add(id, appStoreId, manifest, location, domain, ownerId, portBindings, data, callback) {
function add(id, appStoreId, manifest, location, domain, portBindings, data, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof appStoreId, 'string');
assert(manifest && typeof manifest === 'object');
assert.strictEqual(typeof manifest.version, 'string');
assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof ownerId, 'string');
assert.strictEqual(typeof portBindings, 'object');
assert(data && typeof data === 'object');
assert.strictEqual(typeof callback, 'function');
@@ -279,25 +204,26 @@ function add(id, appStoreId, manifest, location, domain, ownerId, portBindings,
const accessRestriction = data.accessRestriction || null;
const accessRestrictionJson = JSON.stringify(accessRestriction);
const memoryLimit = data.memoryLimit || 0;
const xFrameOptions = data.xFrameOptions || '';
const installationState = data.installationState || exports.ISTATE_PENDING_INSTALL;
const restoreConfigJson = data.restoreConfig ? JSON.stringify(data.restoreConfig) : null; // used when cloning
const cpuShares = data.cpuShares || 512;
const installationState = data.installationState;
const runState = data.runState;
const sso = 'sso' in data ? data.sso : null;
const robotsTxt = 'robotsTxt' in data ? data.robotsTxt : null;
const debugModeJson = data.debugMode ? JSON.stringify(data.debugMode) : null;
const env = data.env || {};
const label = data.label || null;
const tagsJson = data.tags ? JSON.stringify(data.tags) : null;
const mailboxName = data.mailboxName || null;
const mailboxDomain = data.mailboxDomain || null;
const reverseProxyConfigJson = data.reverseProxyConfig ? JSON.stringify(data.reverseProxyConfig) : null;
var queries = [];
queries.push({
query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, accessRestrictionJson, memoryLimit, xFrameOptions,'
+ 'restoreConfigJson, sso, debugModeJson, robotsTxt, ownerId, mailboxName, label, tagsJson) '
query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares, '
+ 'sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson) '
+ ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
args: [ id, appStoreId, manifestJson, installationState, accessRestrictionJson, memoryLimit, xFrameOptions, restoreConfigJson,
sso, debugModeJson, robotsTxt, ownerId, mailboxName, label, tagsJson ]
args: [ id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares,
sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson ]
});
queries.push({
@@ -328,10 +254,19 @@ function add(id, appStoreId, manifest, location, domain, ownerId, portBindings,
});
}
if (data.aliasDomains) {
data.aliasDomains.forEach(function (d) {
queries.push({
query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)',
args: [ id, d.domain, d.subdomain, exports.SUBDOMAIN_TYPE_ALIAS ]
});
});
}
database.transaction(queries, function (error) {
if (error && error.code === 'ER_DUP_ENTRY') return callback(new DatabaseError(DatabaseError.ALREADY_EXISTS, error.message));
if (error && error.code === 'ER_NO_REFERENCED_ROW_2') return callback(new DatabaseError(DatabaseError.NOT_FOUND, 'no such domain'));
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error && error.code === 'ER_DUP_ENTRY') return callback(new BoxError(BoxError.ALREADY_EXISTS, error.message));
if (error && error.code === 'ER_NO_REFERENCED_ROW_2') return callback(new BoxError(BoxError.NOT_FOUND, 'no such domain'));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null);
});
@@ -342,7 +277,7 @@ function exists(id, callback) {
assert.strictEqual(typeof callback, 'function');
database.query('SELECT 1 FROM apps WHERE id=?', [ id ], function (error, result) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
return callback(null, result.length !== 0);
});
@@ -353,7 +288,7 @@ function getPortBindings(id, callback) {
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + PORT_BINDINGS_FIELDS + ' FROM appPortBindings WHERE appId = ?', [ id ], function (error, results) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
var portBindings = { };
for (var i = 0; i < results.length; i++) {
@@ -370,8 +305,8 @@ function delPortBinding(hostPort, type, callback) {
assert.strictEqual(typeof callback, 'function');
database.query('DELETE FROM appPortBindings WHERE hostPort=? AND type=?', [ hostPort, type ], function (error, result) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (result.affectedRows !== 1) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
if (result.affectedRows !== 1) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
callback(null);
});
@@ -385,12 +320,14 @@ function del(id, callback) {
{ query: 'DELETE FROM subdomains WHERE appId = ?', args: [ id ] },
{ query: 'DELETE FROM appPortBindings WHERE appId = ?', args: [ id ] },
{ query: 'DELETE FROM appEnvVars WHERE appId = ?', args: [ id ] },
{ query: 'DELETE FROM appPasswords WHERE identifier = ?', args: [ id ] },
{ query: 'DELETE FROM appMounts WHERE appId = ?', args: [ id ] },
{ query: 'DELETE FROM apps WHERE id = ?', args: [ id ] }
];
database.transaction(queries, function (error, results) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (results[3].affectedRows !== 1) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
if (results[5].affectedRows !== 1) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
callback(null);
});
@@ -406,7 +343,7 @@ function clear(callback) {
database.query.bind(null, 'DELETE FROM appEnvVars'),
database.query.bind(null, 'DELETE FROM apps')
], function (error) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
return callback(null);
});
}
@@ -423,6 +360,7 @@ function updateWithConstraints(id, app, constraints, callback) {
assert(!('portBindings' in app) || typeof app.portBindings === 'object');
assert(!('accessRestriction' in app) || typeof app.accessRestriction === 'object' || app.accessRestriction === '');
assert(!('alternateDomains' in app) || Array.isArray(app.alternateDomains));
assert(!('aliasDomains' in app) || Array.isArray(app.aliasDomains));
assert(!('tags' in app) || Array.isArray(app.tags));
assert(!('env' in app) || typeof app.env === 'object');
@@ -450,22 +388,35 @@ function updateWithConstraints(id, app, constraints, callback) {
}
if ('location' in app && 'domain' in app) { // must be updated together as they are unique together
queries.push({ query: 'UPDATE subdomains SET subdomain = ?, domain = ? WHERE appId = ? AND type = ?', args: [ app.location, app.domain, id, exports.SUBDOMAIN_TYPE_PRIMARY ]});
queries.push({ query: 'DELETE FROM subdomains WHERE appId = ?', args: [ id ]}); // all locations of an app must be updated together
queries.push({ query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, app.domain, app.location, exports.SUBDOMAIN_TYPE_PRIMARY ]});
if ('alternateDomains' in app) {
app.alternateDomains.forEach(function (d) {
queries.push({ query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, exports.SUBDOMAIN_TYPE_REDIRECT ]});
});
}
if ('aliasDomains' in app) {
app.aliasDomains.forEach(function (d) {
queries.push({ query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, exports.SUBDOMAIN_TYPE_ALIAS ]});
});
}
}
if ('alternateDomains' in app) {
queries.push({ query: 'DELETE FROM subdomains WHERE appId = ? AND type = ?', args: [ id, exports.SUBDOMAIN_TYPE_REDIRECT ]});
app.alternateDomains.forEach(function (d) {
queries.push({ query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, exports.SUBDOMAIN_TYPE_REDIRECT ]});
if ('mounts' in app) {
queries.push({ query: 'DELETE FROM appMounts WHERE appId = ?', args: [ id ]});
app.mounts.forEach(function (m) {
queries.push({ query: 'INSERT INTO appMounts (appId, volumeId, readOnly) VALUES (?, ?, ?)', args: [ id, m.volumeId, m.readOnly ]});
});
}
var fields = [ ], values = [ ];
for (var p in app) {
if (p === 'manifest' || p === 'oldConfig' || p === 'updateConfig' || p === 'restoreConfig' || p === 'tags' || p === 'accessRestriction' || p === 'debugMode') {
if (p === 'manifest' || p === 'tags' || p === 'accessRestriction' || p === 'debugMode' || p === 'error' || p === 'reverseProxyConfig' || p === 'servicesConfig') {
fields.push(`${p}Json = ?`);
values.push(JSON.stringify(app[p]));
} else if (p !== 'portBindings' && p !== 'location' && p !== 'domain' && p !== 'alternateDomains' && p !== 'env') {
} else if (p !== 'portBindings' && p !== 'location' && p !== 'domain' && p !== 'alternateDomains' && p !== 'aliasDomains' && p !== 'env' && p !== 'mounts') {
fields.push(p + ' = ?');
values.push(app[p]);
}
@@ -477,15 +428,14 @@ function updateWithConstraints(id, app, constraints, callback) {
}
database.transaction(queries, function (error, results) {
if (error && error.code === 'ER_DUP_ENTRY') return callback(new DatabaseError(DatabaseError.ALREADY_EXISTS, error.message));
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (results[results.length - 1].affectedRows !== 1) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
if (error && error.code === 'ER_DUP_ENTRY') return callback(new BoxError(BoxError.ALREADY_EXISTS, error.message));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
if (results[results.length - 1].affectedRows !== 1) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
return callback(null);
});
}
// not sure if health should influence runState
function setHealth(appId, health, healthTime, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof health, 'string');
@@ -494,60 +444,29 @@ function setHealth(appId, health, healthTime, callback) {
var values = { health, healthTime };
var constraints = 'AND runState NOT LIKE "pending_%" AND installationState = "installed"';
updateWithConstraints(appId, values, constraints, callback);
updateWithConstraints(appId, values, '', callback);
}
function setInstallationCommand(appId, installationState, values, callback) {
function setTask(appId, values, options, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof installationState, 'string');
if (typeof values === 'function') {
callback = values;
values = { };
} else {
assert.strictEqual(typeof values, 'object');
assert.strictEqual(typeof callback, 'function');
}
values.installationState = installationState;
values.installationProgress = '';
// Rules are:
// uninstall is allowed in any state
// force update is allowed in any state including pending_uninstall! (for better or worse)
// restore is allowed from installed or error state or currently restoring
// configure is allowed in installed state or currently configuring or in error state
// update and backup are allowed only in installed state
if (installationState === exports.ISTATE_PENDING_UNINSTALL || installationState === exports.ISTATE_PENDING_FORCE_UPDATE) {
updateWithConstraints(appId, values, '', callback);
} else if (installationState === exports.ISTATE_PENDING_RESTORE) {
updateWithConstraints(appId, values, 'AND (installationState = "installed" OR installationState = "error" OR installationState = "pending_restore")', callback);
} else if (installationState === exports.ISTATE_PENDING_UPDATE || installationState === exports.ISTATE_PENDING_BACKUP) {
updateWithConstraints(appId, values, 'AND installationState = "installed"', callback);
} else if (installationState === exports.ISTATE_PENDING_CONFIGURE) {
updateWithConstraints(appId, values, 'AND (installationState = "installed" OR installationState = "pending_configure" OR installationState = "error")', callback);
} else {
callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, 'invalid installationState'));
}
}
function setRunCommand(appId, runState, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof runState, 'string');
assert.strictEqual(typeof values, 'object');
assert.strictEqual(typeof options, 'object');
assert.strictEqual(typeof callback, 'function');
var values = { runState: runState };
updateWithConstraints(appId, values, 'AND runState NOT LIKE "pending_%" AND installationState = "installed"', callback);
if (!options.requireNullTaskId) return updateWithConstraints(appId, values, '', callback);
if (options.requiredState === null) {
updateWithConstraints(appId, values, 'AND taskId IS NULL', callback);
} else {
updateWithConstraints(appId, values, `AND taskId IS NULL AND installationState = "${options.requiredState}"`, callback);
}
}
function getAppStoreIds(callback) {
assert.strictEqual(typeof callback, 'function');
database.query('SELECT id, appStoreId FROM apps', function (error, results) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null, results);
});
@@ -572,7 +491,7 @@ function setAddonConfig(appId, addonId, env, callback) {
}
database.query(query + queryArgs.join(','), args, function (error) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
return callback(null);
});
@@ -585,7 +504,7 @@ function unsetAddonConfig(appId, addonId, callback) {
assert.strictEqual(typeof callback, 'function');
database.query('DELETE FROM appAddonConfigs WHERE appId = ? AND addonId = ?', [ appId, addonId ], function (error) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null);
});
@@ -596,7 +515,7 @@ function unsetAddonConfigByAppId(appId, callback) {
assert.strictEqual(typeof callback, 'function');
database.query('DELETE FROM appAddonConfigs WHERE appId = ?', [ appId ], function (error) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null);
});
@@ -608,7 +527,7 @@ function getAddonConfig(appId, addonId, callback) {
assert.strictEqual(typeof callback, 'function');
database.query('SELECT name, value FROM appAddonConfigs WHERE appId = ? AND addonId = ?', [ appId, addonId ], function (error, results) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null, results);
});
@@ -619,64 +538,36 @@ function getAddonConfigByAppId(appId, callback) {
assert.strictEqual(typeof callback, 'function');
database.query('SELECT name, value FROM appAddonConfigs WHERE appId = ?', [ appId ], function (error, results) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null, results);
});
}
function getAppIdByAddonConfigValue(addonId, name, value, callback) {
function getAppIdByAddonConfigValue(addonId, namePattern, value, callback) {
assert.strictEqual(typeof addonId, 'string');
assert.strictEqual(typeof name, 'string');
assert.strictEqual(typeof namePattern, 'string');
assert.strictEqual(typeof value, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT appId FROM appAddonConfigs WHERE addonId = ? AND name = ? AND value = ?', [ addonId, name, value ], function (error, results) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (results.length === 0) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
database.query('SELECT appId FROM appAddonConfigs WHERE addonId = ? AND name LIKE ? AND value = ?', [ addonId, namePattern, value ], function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
if (results.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
callback(null, results[0].appId);
});
}
function getAddonConfigByName(appId, addonId, name, callback) {
function getAddonConfigByName(appId, addonId, namePattern, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof addonId, 'string');
assert.strictEqual(typeof name, 'string');
assert.strictEqual(typeof namePattern, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT value FROM appAddonConfigs WHERE appId = ? AND addonId = ? AND name = ?', [ appId, addonId, name ], function (error, results) {
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (results.length === 0) return callback(new DatabaseError(DatabaseError.NOT_FOUND));
database.query('SELECT value FROM appAddonConfigs WHERE appId = ? AND addonId = ? AND name LIKE ?', [ appId, addonId, namePattern ], function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
if (results.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
callback(null, results[0].value);
});
}
function setOwner(appId, ownerId, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof ownerId, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('UPDATE apps SET ownerId=? WHERE appId=?', [ ownerId, appId ], function (error, results) {
if (error && error.code === 'ER_NO_REFERENCED_ROW_2') return callback(new DatabaseError(DatabaseError.NOT_FOUND, 'No such user'));
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
if (results.length === 0) return callback(new DatabaseError(DatabaseError.NOT_FOUND, 'No such app'));
callback(null);
});
}
function transferOwnership(oldOwnerId, newOwnerId, callback) {
assert.strictEqual(typeof oldOwnerId, 'string');
assert.strictEqual(typeof newOwnerId, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('UPDATE apps SET ownerId=? WHERE ownerId=?', [ newOwnerId, oldOwnerId ], function (error) {
if (error && error.code === 'ER_NO_REFERENCED_ROW_2') return callback(new DatabaseError(DatabaseError.NOT_FOUND, 'No such user'));
if (error) return callback(new DatabaseError(DatabaseError.INTERNAL_ERROR, error));
callback(null);
});
}
+27 -39
View File
@@ -5,7 +5,7 @@ var appdb = require('./appdb.js'),
assert = require('assert'),
async = require('async'),
auditSource = require('./auditsource.js'),
DatabaseError = require('./databaseerror.js'),
BoxError = require('./boxerror.js'),
debug = require('debug')('box:apphealthmonitor'),
docker = require('./docker.js'),
eventlog = require('./eventlog.js'),
@@ -14,19 +14,20 @@ var appdb = require('./appdb.js'),
util = require('util');
exports = module.exports = {
run: run
run
};
const HEALTHCHECK_INTERVAL = 10 * 1000; // every 10 seconds. this needs to be small since the UI makes only healthy apps clickable
const UNHEALTHY_THRESHOLD = 10 * 60 * 1000; // 10 minutes
const OOM_EVENT_LIMIT = 60 * 60 * 1000; // 60 minutes
const gStartTime = new Date(); // time when apphealthmonitor was started
let gLastOomMailTime = Date.now() - (5 * 60 * 1000); // pretend we sent email 5 minutes ago
function debugApp(app) {
assert(typeof app === 'object');
debug(app.fqdn + ' ' + app.manifest.id + ' ' + util.format.apply(util, Array.prototype.slice.call(arguments, 1)) + ' - ' + app.id);
debug(app.fqdn + ' ' + util.format.apply(util, Array.prototype.slice.call(arguments, 1)) + ' - ' + app.id);
}
function setHealth(app, health, callback) {
@@ -34,18 +35,19 @@ function setHealth(app, health, callback) {
assert.strictEqual(typeof health, 'string');
assert.strictEqual(typeof callback, 'function');
let now = new Date(), healthTime = app.healthTime, curHealth = app.health;
let now = new Date(), curHealth = app.health;
let healthTime = gStartTime > app.healthTime ? gStartTime : app.healthTime; // on box restart, clamp value to start time
if (health === appdb.HEALTH_HEALTHY) {
if (health === apps.HEALTH_HEALTHY) {
healthTime = now;
if (curHealth && curHealth !== appdb.HEALTH_HEALTHY) { // app starts out with null health
if (curHealth && curHealth !== apps.HEALTH_HEALTHY) { // app starts out with null health
debugApp(app, 'app switched from %s to healthy', curHealth);
// do not send mails for dev apps
if (!app.debugMode) eventlog.add(eventlog.ACTION_APP_UP, auditSource.HEALTH_MONITOR, { app: app });
}
} else if (Math.abs(now - healthTime) > UNHEALTHY_THRESHOLD) {
if (curHealth === appdb.HEALTH_HEALTHY) {
if (curHealth === apps.HEALTH_HEALTHY) {
debugApp(app, 'marking as unhealthy since not seen for more than %s minutes', UNHEALTHY_THRESHOLD/(60 * 1000));
// do not send mails for dev apps
@@ -57,7 +59,7 @@ function setHealth(app, health, callback) {
}
appdb.setHealth(app.id, health, healthTime, function (error) {
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null); // app uninstalled?
if (error && error.reason === BoxError.NOT_FOUND) return callback(null); // app uninstalled?
if (error) return callback(error);
app.health = health;
@@ -72,44 +74,33 @@ function checkAppHealth(app, callback) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof callback, 'function');
if (app.installationState !== appdb.ISTATE_INSTALLED || app.runState !== appdb.RSTATE_RUNNING) {
debugApp(app, 'skipped. istate:%s rstate:%s', app.installationState, app.runState);
if (app.installationState !== apps.ISTATE_INSTALLED || app.runState !== apps.RSTATE_RUNNING) {
return callback(null);
}
const manifest = app.manifest;
docker.inspect(app.containerId, function (error, data) {
if (error || !data || !data.State) {
debugApp(app, 'Error inspecting container');
return setHealth(app, appdb.HEALTH_ERROR, callback);
}
if (data.State.Running !== true) {
debugApp(app, 'exited');
return setHealth(app, appdb.HEALTH_DEAD, callback);
}
if (error || !data || !data.State) return setHealth(app, apps.HEALTH_ERROR, callback);
if (data.State.Running !== true) return setHealth(app, apps.HEALTH_DEAD, callback);
// non-appstore apps may not have healthCheckPath
if (!manifest.healthCheckPath) return setHealth(app, appdb.HEALTH_HEALTHY, callback);
if (!manifest.healthCheckPath) return setHealth(app, apps.HEALTH_HEALTHY, callback);
// poll through docker network instead of nginx to bypass any potential oauth proxy
var healthCheckUrl = 'http://127.0.0.1:' + app.httpPort + manifest.healthCheckPath;
const healthCheckUrl = `http://${app.containerIp}:${manifest.httpPort}${manifest.healthCheckPath}`;
superagent
.get(healthCheckUrl)
.set('Host', app.fqdn) // required for some apache configs with rewrite rules
.set('User-Agent', 'Mozilla') // required for some apps (e.g. minio)
.set('User-Agent', 'Mozilla (CloudronHealth)') // required for some apps (e.g. minio)
.redirects(0)
.timeout(HEALTHCHECK_INTERVAL)
.end(function (error, res) {
if (error && !error.response) {
debugApp(app, 'not alive (network error): %s', error.message);
setHealth(app, appdb.HEALTH_UNHEALTHY, callback);
} else if (res.statusCode >= 400) { // 2xx and 3xx are ok
debugApp(app, 'not alive : %s', error || res.status);
setHealth(app, appdb.HEALTH_UNHEALTHY, callback);
setHealth(app, apps.HEALTH_UNHEALTHY, callback);
} else if (res.statusCode >= 403) { // 2xx and 3xx are ok. even 401 and 403 are ok for now (for WP sites)
setHealth(app, apps.HEALTH_UNHEALTHY, callback);
} else {
setHealth(app, appdb.HEALTH_HEALTHY, callback);
setHealth(app, apps.HEALTH_HEALTHY, callback);
}
});
});
@@ -129,7 +120,7 @@ function getContainerInfo(containerId, callback) {
/*
OOM can be tested using stress tool like so:
docker run -ti -m 100M cloudron/base:0.10.0 /bin/bash
docker run -ti -m 100M cloudron/base:2.0.0 /bin/bash
apt-get update && apt-get install stress
stress --vm 1 --vm-bytes 200M --vm-hang 0
*/
@@ -180,17 +171,14 @@ function processDockerEvents(intervalSecs, callback) {
function processApp(callback) {
assert.strictEqual(typeof callback, 'function');
apps.getAll(function (error, result) {
apps.getAll(function (error, allApps) {
if (error) return callback(error);
async.each(result, checkAppHealth, function (error) {
if (error) console.error(error);
async.each(allApps, checkAppHealth, function (error) {
const alive = allApps
.filter(function (a) { return a.installationState === apps.ISTATE_INSTALLED && a.runState === apps.RSTATE_RUNNING && a.health === apps.HEALTH_HEALTHY; });
var alive = result
.filter(function (a) { return a.installationState === appdb.ISTATE_INSTALLED && a.runState === appdb.RSTATE_RUNNING && a.health === appdb.HEALTH_HEALTHY; })
.map(function (a) { return (a.location || 'naked_domain') + '|' + a.manifest.id; }).join(', ');
debug('apps alive: [%s]', alive);
debug(`app health: ${alive.length} alive / ${allApps.length - alive.length} dead.` + (error ? ` ${error.reason}` : ''));
callback(null);
});
@@ -205,7 +193,7 @@ function run(intervalSecs, callback) {
processApp, // this is first because docker.getEvents seems to get 'stuck' sometimes
processDockerEvents.bind(null, intervalSecs)
], function (error) {
if (error) debug(error);
if (error) debug(`run: could not check app health. ${error.message}`);
callback();
});
+1451 -804
View File
File diff suppressed because it is too large Load Diff
+254 -237
View File
@@ -1,83 +1,87 @@
'use strict';
exports = module.exports = {
getApps: getApps,
getApp: getApp,
getAppVersion: getAppVersion,
getFeatures,
registerWithLoginCredentials: registerWithLoginCredentials,
registerWithLicense: registerWithLicense,
getApps,
getApp,
getAppVersion,
purchaseApp: purchaseApp,
unpurchaseApp: unpurchaseApp,
trackBeginSetup,
trackFinishedSetup,
getSubscription: getSubscription,
isFreePlan: isFreePlan,
registerWithLoginCredentials,
sendAliveStatus: sendAliveStatus,
purchaseApp,
unpurchaseApp,
getAppUpdate: getAppUpdate,
getBoxUpdate: getBoxUpdate,
getUserToken,
getSubscription,
isFreePlan,
createTicket: createTicket,
getAppUpdate,
getBoxUpdate,
AppstoreError: AppstoreError
createTicket
};
var apps = require('./apps.js'),
assert = require('assert'),
async = require('async'),
config = require('./config.js'),
custom = require('./custom.js'),
BoxError = require('./boxerror.js'),
constants = require('./constants.js'),
debug = require('debug')('box:appstore'),
domains = require('./domains.js'),
eventlog = require('./eventlog.js'),
mail = require('./mail.js'),
os = require('os'),
path = require('path'),
paths = require('./paths.js'),
safe = require('safetydance'),
semver = require('semver'),
settings = require('./settings.js'),
superagent = require('superagent'),
support = require('./support.js'),
util = require('util');
function AppstoreError(reason, errorOrMessage) {
assert.strictEqual(typeof reason, 'string');
assert(errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined');
// These are the default options and will be adjusted once a subscription state is obtained
// Keep in sync with appstore/routes/cloudrons.js
let gFeatures = {
userMaxCount: 5,
userGroups: false,
userRoles: false,
domainMaxCount: 1,
externalLdap: false,
privateDockerRegistry: false,
branding: false,
support: false,
directoryConfig: false,
mailboxMaxCount: 5,
emailPremium: false
};
Error.call(this);
Error.captureStackTrace(this, this.constructor);
// attempt to load feature cache in case appstore would be down
let tmp = safe.JSON.parse(safe.fs.readFileSync(paths.FEATURES_INFO_FILE, 'utf8'));
if (tmp) gFeatures = tmp;
this.name = this.constructor.name;
this.reason = reason;
if (typeof errorOrMessage === 'undefined') {
this.message = reason;
} else if (typeof errorOrMessage === 'string') {
this.message = errorOrMessage;
} else {
this.message = 'Internal error';
this.nestedError = errorOrMessage;
}
function getFeatures() {
return gFeatures;
}
util.inherits(AppstoreError, Error);
AppstoreError.INTERNAL_ERROR = 'Internal Error';
AppstoreError.EXTERNAL_ERROR = 'External Error';
AppstoreError.ALREADY_EXISTS = 'Already Exists';
AppstoreError.ACCESS_DENIED = 'Access Denied';
AppstoreError.NOT_FOUND = 'Not Found';
AppstoreError.PLAN_LIMIT = 'Plan limit reached'; // upstream 402 (subsciption_expired and subscription_required)
AppstoreError.LICENSE_ERROR = 'License Error'; // upstream 422 (no license, invalid license)
AppstoreError.INVALID_TOKEN = 'Invalid token'; // upstream 401 (invalid token)
AppstoreError.NOT_REGISTERED = 'Not registered'; // upstream 412 (no token, not set yet)
AppstoreError.ALREADY_REGISTERED = 'Already registered';
var NOOP_CALLBACK = function (error) { if (error) debug(error); };
function isAppAllowed(appstoreId, listingConfig) {
assert.strictEqual(typeof listingConfig, 'object');
assert.strictEqual(typeof appstoreId, 'string');
if (listingConfig.blacklist && listingConfig.blacklist.includes(appstoreId)) return false;
if (listingConfig.whitelist) return listingConfig.whitelist.includes(appstoreId);
return true;
}
function getCloudronToken(callback) {
assert.strictEqual(typeof callback, 'function');
settings.getCloudronToken(function (error, token) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
if (!token) return callback(new AppstoreError(AppstoreError.NOT_REGISTERED));
if (error) return callback(error);
if (!token) return callback(new BoxError(BoxError.LICENSE_ERROR, 'Missing token'));
callback(null, token);
});
@@ -95,11 +99,11 @@ function login(email, password, totpToken, callback) {
totpToken: totpToken
};
const url = config.apiServerOrigin() + '/api/v1/login';
const url = settings.apiServerOrigin() + '/api/v1/login';
superagent.post(url).send(data).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.ACCESS_DENIED));
if (result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `login status code: ${result.statusCode}`));
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `login status code: ${result.statusCode}`));
callback(null, result.body); // { userId, accessToken }
});
@@ -115,31 +119,54 @@ function registerUser(email, password, callback) {
password: password,
};
const url = config.apiServerOrigin() + '/api/v1/register_user';
const url = settings.apiServerOrigin() + '/api/v1/register_user';
superagent.post(url).send(data).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (result.statusCode === 409) return callback(new AppstoreError(AppstoreError.ALREADY_EXISTS));
if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `register status code: ${result.statusCode}`));
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode === 409) return callback(new BoxError(BoxError.ALREADY_EXISTS, error.message));
if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `register status code: ${result.statusCode}`));
callback(null);
});
}
function getUserToken(callback) {
assert.strictEqual(typeof callback, 'function');
if (settings.isDemo()) return callback(new BoxError(BoxError.BAD_FIELD, 'Not allowed in demo mode'));
getCloudronToken(function (error, token) {
if (error) return callback(error);
const url = `${settings.apiServerOrigin()}/api/v1/user_token`;
superagent.post(url).send({}).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `getUserToken status code: ${result.status}`));
callback(null, result.body.accessToken);
});
});
}
function getSubscription(callback) {
assert.strictEqual(typeof callback, 'function');
getCloudronToken(function (error, token) {
if (error) return callback(error);
const url = config.apiServerOrigin() + '/api/v1/subscription';
const url = settings.apiServerOrigin() + '/api/v1/subscription';
superagent.get(url).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR));
if (result.statusCode === 502) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `Stripe error: ${error.message}`));
if (result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `Unknown error: ${error.message}`));
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR));
if (result.statusCode === 502) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Stripe error: ${error.message}`));
if (result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Unknown error: ${error.message}`));
callback(null, result.body); // { email, subscription }
// update the features cache
gFeatures = result.body.features;
safe.fs.writeFileSync(paths.FEATURES_INFO_FILE, JSON.stringify(gFeatures), 'utf8');
callback(null, result.body);
});
});
}
@@ -158,16 +185,16 @@ function purchaseApp(data, callback) {
getCloudronToken(function (error, token) {
if (error) return callback(error);
const url = `${config.apiServerOrigin()}/api/v1/cloudronapps`;
const url = `${settings.apiServerOrigin()}/api/v1/cloudronapps`;
superagent.post(url).send(data).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (result.statusCode === 404) return callback(new AppstoreError(AppstoreError.NOT_FOUND)); // appstoreId does not exist
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode === 402) return callback(new AppstoreError(AppstoreError.PLAN_LIMIT, result.body.message));
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode === 404) return callback(new BoxError(BoxError.NOT_FOUND)); // appstoreId does not exist
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode === 402) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
// 200 if already purchased, 201 is newly purchased
if (result.statusCode !== 201 && result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App purchase failed. %s %j', result.status, result.body)));
if (result.statusCode !== 201 && result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App purchase failed. %s %j', result.status, result.body)));
callback(null);
});
@@ -183,19 +210,19 @@ function unpurchaseApp(appId, data, callback) {
getCloudronToken(function (error, token) {
if (error) return callback(error);
const url = `${config.apiServerOrigin()}/api/v1/cloudronapps/${appId}`;
const url = `${settings.apiServerOrigin()}/api/v1/cloudronapps/${appId}`;
superagent.get(url).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode === 404) return callback(null); // was never purchased
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
if (result.statusCode !== 201 && result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body)));
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
if (result.statusCode !== 201 && result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body)));
superagent.del(url).send(data).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error));
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode !== 204) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body)));
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error));
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode !== 204) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body)));
callback(null);
});
@@ -203,143 +230,70 @@ function unpurchaseApp(appId, data, callback) {
});
}
function sendAliveStatus(callback) {
callback = callback || NOOP_CALLBACK;
var allSettings, allDomains, mailDomains, loginEvents;
async.series([
function (callback) {
settings.getAll(function (error, result) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
allSettings = result;
callback();
});
},
function (callback) {
domains.getAll(function (error, result) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
allDomains = result;
callback();
});
},
function (callback) {
mail.getDomains(function (error, result) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
mailDomains = result;
callback();
});
},
function (callback) {
eventlog.getAllPaged([ eventlog.ACTION_USER_LOGIN ], null, 1, 1, function (error, result) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
loginEvents = result;
callback();
});
}
], function (error) {
if (error) return callback(error);
var backendSettings = {
backupConfig: {
provider: allSettings[settings.BACKUP_CONFIG_KEY].provider,
hardlinks: !allSettings[settings.BACKUP_CONFIG_KEY].noHardlinks
},
domainConfig: {
count: allDomains.length,
domains: Array.from(new Set(allDomains.map(function (d) { return { domain: d.domain, provider: d.provider }; })))
},
mailConfig: {
outboundCount: mailDomains.length,
inboundCount: mailDomains.filter(function (d) { return d.enabled; }).length,
catchAllCount: mailDomains.filter(function (d) { return d.catchAll.length !== 0; }).length,
relayProviders: Array.from(new Set(mailDomains.map(function (d) { return d.relay.provider; })))
},
appAutoupdatePattern: allSettings[settings.APP_AUTOUPDATE_PATTERN_KEY],
boxAutoupdatePattern: allSettings[settings.BOX_AUTOUPDATE_PATTERN_KEY],
timeZone: allSettings[settings.TIME_ZONE_KEY],
};
var data = {
version: config.version(),
adminFqdn: config.adminFqdn(),
provider: config.provider(),
backendSettings: backendSettings,
machine: {
cpus: os.cpus(),
totalmem: os.totalmem()
},
events: {
lastLogin: loginEvents[0] ? (new Date(loginEvents[0].creationTime).getTime()) : 0
}
};
getCloudronToken(function (error, token) {
if (error) return callback(error);
const url = `${config.apiServerOrigin()}/api/v1/alive`;
superagent.post(url).send(data).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error));
if (result.statusCode === 404) return callback(new AppstoreError(AppstoreError.NOT_FOUND));
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Sending alive status failed. %s %j', result.status, result.body)));
callback(null);
});
});
});
}
function getBoxUpdate(callback) {
function getBoxUpdate(options, callback) {
assert.strictEqual(typeof options, 'object');
assert.strictEqual(typeof callback, 'function');
getCloudronToken(function (error, token) {
if (error) return callback(error);
const url = `${config.apiServerOrigin()}/api/v1/boxupdate`;
const url = `${settings.apiServerOrigin()}/api/v1/boxupdate`;
superagent.get(url).query({ accessToken: token, boxVersion: config.version() }).timeout(10 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
if (result.statusCode === 204) return callback(null); // no update
if (result.statusCode !== 200 || !result.body) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
const query = {
accessToken: token,
boxVersion: constants.VERSION,
automatic: options.automatic
};
superagent.get(url).query(query).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
if (result.statusCode === 204) return callback(null, null); // no update
if (result.statusCode !== 200 || !result.body) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
var updateInfo = result.body;
if (!semver.valid(updateInfo.version) || semver.gt(config.version(), updateInfo.version)) {
return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Invalid update version: %s %s', result.statusCode, result.text)));
if (!semver.valid(updateInfo.version) || semver.gt(constants.VERSION, updateInfo.version)) {
return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Update version invalid or is a downgrade: %s %s', result.statusCode, result.text)));
}
// updateInfo: { version, changelog, sourceTarballUrl, sourceTarballSigUrl, boxVersionsUrl, boxVersionsSigUrl }
if (!updateInfo.version || typeof updateInfo.version !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad version): %s %s', result.statusCode, result.text)));
if (!updateInfo.changelog || !Array.isArray(updateInfo.changelog)) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad version): %s %s', result.statusCode, result.text)));
if (!updateInfo.sourceTarballUrl || typeof updateInfo.sourceTarballUrl !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad sourceTarballUrl): %s %s', result.statusCode, result.text)));
if (!updateInfo.sourceTarballSigUrl || typeof updateInfo.sourceTarballSigUrl !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad sourceTarballSigUrl): %s %s', result.statusCode, result.text)));
if (!updateInfo.boxVersionsUrl || typeof updateInfo.boxVersionsUrl !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad boxVersionsUrl): %s %s', result.statusCode, result.text)));
if (!updateInfo.boxVersionsSigUrl || typeof updateInfo.boxVersionsSigUrl !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad boxVersionsSigUrl): %s %s', result.statusCode, result.text)));
if (!updateInfo.version || typeof updateInfo.version !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad version): %s %s', result.statusCode, result.text)));
if (!updateInfo.changelog || !Array.isArray(updateInfo.changelog)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad version): %s %s', result.statusCode, result.text)));
if (!updateInfo.sourceTarballUrl || typeof updateInfo.sourceTarballUrl !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad sourceTarballUrl): %s %s', result.statusCode, result.text)));
if (!updateInfo.sourceTarballSigUrl || typeof updateInfo.sourceTarballSigUrl !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad sourceTarballSigUrl): %s %s', result.statusCode, result.text)));
if (!updateInfo.boxVersionsUrl || typeof updateInfo.boxVersionsUrl !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad boxVersionsUrl): %s %s', result.statusCode, result.text)));
if (!updateInfo.boxVersionsSigUrl || typeof updateInfo.boxVersionsSigUrl !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad boxVersionsSigUrl): %s %s', result.statusCode, result.text)));
callback(null, updateInfo);
});
});
}
function getAppUpdate(app, callback) {
function getAppUpdate(app, options, callback) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof options, 'object');
assert.strictEqual(typeof callback, 'function');
getCloudronToken(function (error, token) {
if (error) return callback(error);
const url = `${config.apiServerOrigin()}/api/v1/appupdate`;
const url = `${settings.apiServerOrigin()}/api/v1/appupdate`;
const query = {
accessToken: token,
boxVersion: constants.VERSION,
appId: app.appStoreId,
appVersion: app.manifest.version,
automatic: options.automatic
};
superagent.get(url).query({ accessToken: token, boxVersion: config.version(), appId: app.appStoreId, appVersion: app.manifest.version }).timeout(10 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error));
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
superagent.get(url).query(query).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error));
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
if (result.statusCode === 204) return callback(null); // no update
if (result.statusCode !== 200 || !result.body) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
if (result.statusCode !== 200 || !result.body) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
const updateInfo = result.body;
@@ -349,10 +303,12 @@ function getAppUpdate(app, callback) {
// do some sanity checks
if (!safe.query(updateInfo, 'manifest.version') || semver.gt(curAppVersion, safe.query(updateInfo, 'manifest.version'))) {
debug('Skipping malformed update of app %s version: %s. got %j', app.id, curAppVersion, updateInfo);
return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Malformed update: %s %s', result.statusCode, result.text)));
return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Malformed update: %s %s', result.statusCode, result.text)));
}
// { id, creationDate, manifest }
updateInfo.unstable = !!updateInfo.unstable;
// { id, creationDate, manifest, unstable }
callback(null, updateInfo);
});
});
@@ -362,23 +318,23 @@ function registerCloudron(data, callback) {
assert.strictEqual(typeof data, 'object');
assert.strictEqual(typeof callback, 'function');
const url = `${config.apiServerOrigin()}/api/v1/register_cloudron`;
const url = `${settings.apiServerOrigin()}/api/v1/register_cloudron`;
superagent.post(url).send(data).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `Unable to register cloudron: ${error.message}`));
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Unable to register cloudron: ${result.statusCode} ${error.message}`));
// cloudronId, token, licenseKey
if (!result.body.cloudronId) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, 'Invalid response - no cloudron id'));
if (!result.body.cloudronToken) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, 'Invalid response - no token'));
if (!result.body.licenseKey) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, 'Invalid response - no license'));
if (!result.body.cloudronId) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Invalid response - no cloudron id'));
if (!result.body.cloudronToken) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Invalid response - no token'));
if (!result.body.licenseKey) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Invalid response - no license'));
async.series([
settings.setCloudronId.bind(null, result.body.cloudronId),
settings.setCloudronToken.bind(null, result.body.cloudronToken),
settings.setLicenseKey.bind(null, result.body.licenseKey),
], function (error) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
if (error) return callback(error);
debug(`registerCloudron: Cloudron registered with id ${result.body.cloudronId}`);
@@ -387,14 +343,30 @@ function registerCloudron(data, callback) {
});
}
function registerWithLicense(license, callback) {
assert.strictEqual(typeof license, 'string');
assert.strictEqual(typeof callback, 'function');
// This works without a Cloudron token as this Cloudron was not yet registered
let gBeginSetupAlreadyTracked = false;
function trackBeginSetup() {
// avoid browser reload double tracking, not perfect since box might restart, but covers most cases and is simple
if (gBeginSetupAlreadyTracked) return;
gBeginSetupAlreadyTracked = true;
getCloudronToken(function (error, token) {
if (token) return callback(new AppstoreError(AppstoreError.ALREADY_REGISTERED));
const url = `${settings.apiServerOrigin()}/api/v1/helper/setup_begin`;
registerCloudron({ license }, callback);
superagent.post(url).send({}).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return debug(`trackBeginSetup: ${error.message}`);
if (result.statusCode !== 200) return debug(`trackBeginSetup: ${result.statusCode} ${error.message}`);
});
}
// This works without a Cloudron token as this Cloudron was not yet registered
function trackFinishedSetup(domain) {
assert.strictEqual(typeof domain, 'string');
const url = `${settings.apiServerOrigin()}/api/v1/helper/setup_finished`;
superagent.post(url).send({ domain }).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return debug(`trackFinishedSetup: ${error.message}`);
if (result.statusCode !== 200) return debug(`trackFinishedSetup: ${result.statusCode} ${error.message}`);
});
}
@@ -409,7 +381,7 @@ function registerWithLoginCredentials(options, callback) {
}
getCloudronToken(function (error, token) {
if (token) return callback(new AppstoreError(AppstoreError.ALREADY_REGISTERED));
if (token) return callback(new BoxError(BoxError.CONFLICT, 'Cloudron is already registered'));
maybeSignup(function (error) {
if (error) return callback(error);
@@ -417,19 +389,20 @@ function registerWithLoginCredentials(options, callback) {
login(options.email, options.password, options.totpToken || '', function (error, result) {
if (error) return callback(error);
registerCloudron({ domain: config.adminDomain(), accessToken: result.accessToken }, callback);
registerCloudron({ domain: settings.adminDomain(), accessToken: result.accessToken, version: constants.VERSION, purpose: options.purpose || '' }, callback);
});
});
});
}
function createTicket(info, callback) {
function createTicket(info, auditSource, callback) {
assert.strictEqual(typeof info, 'object');
assert.strictEqual(typeof info.email, 'string');
assert.strictEqual(typeof info.displayName, 'string');
assert.strictEqual(typeof info.type, 'string');
assert.strictEqual(typeof info.subject, 'string');
assert.strictEqual(typeof info.description, 'string');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
function collectAppInfoIfNeeded(callback) {
@@ -437,24 +410,55 @@ function createTicket(info, callback) {
apps.get(info.appId, callback);
}
function enableSshIfNeeded(callback) {
if (!info.enableSshSupport) return callback();
support.enableRemoteSupport(true, auditSource, function (error) {
// ensure we can at least get the ticket through
if (error) debug('Unable to enable SSH support.', error);
callback();
});
}
getCloudronToken(function (error, token) {
if (error) return callback(error);
collectAppInfoIfNeeded(function (error, result) {
if (error) console.error('Unable to get app info', error);
if (result) info.app = result;
enableSshIfNeeded(function (error) {
if (error) return callback(error);
let url = config.apiServerOrigin() + '/api/v1/ticket';
collectAppInfoIfNeeded(function (error, app) {
if (error) return callback(error);
if (app) info.app = app;
info.supportEmail = custom.supportEmail(); // destination address for tickets
info.supportEmail = constants.SUPPORT_EMAIL; // destination address for tickets
superagent.post(url).query({ accessToken: token }).send(info).timeout(10 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
var req = superagent.post(`${settings.apiServerOrigin()}/api/v1/ticket`)
.query({ accessToken: token })
.timeout(30 * 1000);
callback(null);
// either send as JSON through body or as multipart, depending on attachments
if (info.app) {
req.field('infoJSON', JSON.stringify(info));
apps.getLocalLogfilePaths(info.app).forEach(function (filePath) {
var logs = safe.child_process.execSync(`tail --lines=1000 ${filePath}`);
if (logs) req.attach(path.basename(filePath), logs, path.basename(filePath));
});
} else {
req.send(info);
}
req.end(function (error, result) {
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
eventlog.add(eventlog.ACTION_SUPPORT_TICKET, auditSource, info);
callback(null, { message: `An email was sent to ${constants.SUPPORT_EMAIL}. We will get back shortly!` });
});
});
});
});
@@ -467,16 +471,23 @@ function getApps(callback) {
if (error) return callback(error);
settings.getUnstableAppsConfig(function (error, unstable) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
const url = `${config.apiServerOrigin()}/api/v1/apps`;
superagent.get(url).query({ accessToken: token, boxVersion: config.version(), unstable: unstable }).timeout(10 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (result.statusCode === 403 || result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
if (result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App listing failed. %s %j', result.status, result.body)));
if (!result.body.apps) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
if (error) return callback(error);
callback(null, result.body.apps);
const url = `${settings.apiServerOrigin()}/api/v1/apps`;
superagent.get(url).query({ accessToken: token, boxVersion: constants.VERSION, unstable: unstable }).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode === 403 || result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
if (result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App listing failed. %s %j', result.status, result.body)));
if (!result.body.apps) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
settings.getAppstoreListingConfig(function (error, listingConfig) {
if (error) return callback(error);
const filteredApps = result.body.apps.filter(app => isAppAllowed(app.id, listingConfig));
callback(null, filteredApps);
});
});
});
});
@@ -487,20 +498,26 @@ function getAppVersion(appId, version, callback) {
assert.strictEqual(typeof version, 'string');
assert.strictEqual(typeof callback, 'function');
getCloudronToken(function (error, token) {
settings.getAppstoreListingConfig(function (error, listingConfig) {
if (error) return callback(error);
let url = `${config.apiServerOrigin()}/api/v1/apps/${appId}`;
if (version !== 'latest') url += `/versions/${version}`;
if (!isAppAllowed(appId, listingConfig)) return callback(new BoxError(BoxError.FEATURE_DISABLED));
superagent.get(url).query({ accessToken: token }).timeout(10 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
if (result.statusCode === 403 || result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
if (result.statusCode === 404) return callback(new AppstoreError(AppstoreError.NOT_FOUND));
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
if (result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App fetch failed. %s %j', result.status, result.body)));
getCloudronToken(function (error, token) {
if (error) return callback(error);
callback(null, result.body);
let url = `${settings.apiServerOrigin()}/api/v1/apps/${appId}`;
if (version !== 'latest') url += `/versions/${version}`;
superagent.get(url).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
if (result.statusCode === 403 || result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
if (result.statusCode === 404) return callback(new BoxError(BoxError.NOT_FOUND));
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
if (result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App fetch failed. %s %j', result.status, result.body)));
callback(null, result.body);
});
});
});
}
+639 -517
View File
File diff suppressed because it is too large Load Diff
+96
View File
@@ -0,0 +1,96 @@
'use strict';
exports = module.exports = {
scheduleTask
};
let assert = require('assert'),
BoxError = require('./boxerror.js'),
debug = require('debug')('box:apptaskmanager'),
fs = require('fs'),
locker = require('./locker.js'),
safe = require('safetydance'),
path = require('path'),
paths = require('./paths.js'),
scheduler = require('./scheduler.js'),
services = require('./services.js'),
tasks = require('./tasks.js');
let gActiveTasks = { }; // indexed by app id
let gPendingTasks = [ ];
let gInitialized = false;
const TASK_CONCURRENCY = 3;
const NOOP_CALLBACK = function (error) { if (error) debug(error); };
function waitText(lockOperation) {
if (lockOperation === locker.OP_BOX_UPDATE) return 'Waiting for Cloudron to finish updating. See the Settings view';
if (lockOperation === locker.OP_PLATFORM_START) return 'Waiting for Cloudron to initialize';
if (lockOperation === locker.OP_FULL_BACKUP) return 'Wait for Cloudron to finish backup. See the Backups view';
return ''; // cannot happen
}
function initializeSync() {
gInitialized = true;
locker.on('unlocked', startNextTask);
}
// callback is called when task is finished
function scheduleTask(appId, taskId, options, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof taskId, 'string');
assert.strictEqual(typeof options, 'object');
assert.strictEqual(typeof callback, 'function');
if (!gInitialized) initializeSync();
if (appId in gActiveTasks) {
return callback(new BoxError(BoxError.CONFLICT, `Task for %s is already active: ${appId}`));
}
if (Object.keys(gActiveTasks).length >= TASK_CONCURRENCY) {
debug(`Reached concurrency limit, queueing task id ${taskId}`);
tasks.update(taskId, { percent: 1, message: 'Waiting for other app tasks to complete' }, NOOP_CALLBACK);
gPendingTasks.push({ appId, taskId, options, callback });
return;
}
var lockError = locker.recursiveLock(locker.OP_APPTASK);
if (lockError) {
debug(`Could not get lock. ${lockError.message}, queueing task id ${taskId}`);
tasks.update(taskId, { percent: 1, message: waitText(lockError.operation) }, NOOP_CALLBACK);
gPendingTasks.push({ appId, taskId, options, callback });
return;
}
gActiveTasks[appId] = {};
const logFile = path.join(paths.LOG_DIR, appId, 'apptask.log');
if (!fs.existsSync(path.dirname(logFile))) safe.fs.mkdirSync(path.dirname(logFile)); // ensure directory
scheduler.suspendJobs(appId);
tasks.startTask(taskId, Object.assign(options, { logFile }), function (error, result) {
callback(error, result);
delete gActiveTasks[appId];
locker.unlock(locker.OP_APPTASK); // unlock event will trigger next task
// post app task hooks
services.rebuildService('sftp', error => { if (error) debug('Unable to rebuild sftp:', error); });
scheduler.resumeJobs(appId);
});
}
function startNextTask() {
if (gPendingTasks.length === 0) return;
assert(Object.keys(gActiveTasks).length < TASK_CONCURRENCY);
const t = gPendingTasks.shift();
scheduleTask(t.appId, t.taskId, t.options, t.callback);
}

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