diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..e4fc7d9e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +*.py[co] + +# Packages +*.egg +*.egg-info +*.swp +*.swo +dist +build +eggs +parts +bin +cache +var +sdist +develop-eggs +.installed.cfg +log + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg diff --git a/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml similarity index 87% rename from actionsmap/yunohost.yml rename to data/actionsmap/yunohost.yml index 21fad7d90..969f4ebb6 100644 --- a/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -98,43 +98,43 @@ user: username: help: The unique username to create extra: - pattern: - - '^[a-z0-9_]+$' - - pattern_username + pattern: &pattern_username + - !!str ^[a-z0-9_]+$ + - "pattern_username" -f: full: --firstname extra: ask: ask_firstname required: True - pattern: - - "^([^\\W\\d_]{2,30}[ ,.'-]{0,3})+$" - - pattern_firstname + pattern: &pattern_firstname + - !!str ^([^\W\d_]{2,30}[ ,.'-]{0,3})+$ + - "pattern_firstname" -l: full: --lastname extra: ask: ask_lastname required: True - pattern: - - "^([^\\W\\d_]{2,30}[ ,.']{0,3})+$" - - pattern_lastname + pattern: &pattern_lastname + - !!str ^([^\W\d_]{2,30}[ ,.']{0,3})+$ + - "pattern_lastname" -m: full: --mail help: Main unique email address extra: ask: ask_email required: True - pattern: - - '^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,6}$' - - pattern_email + pattern: &pattern_email + - !!str ^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,6}$ + - "pattern_email" -p: full: --password help: User password extra: password: ask_password required: True - pattern: - - '^.{3,}$' - - pattern_password + pattern: &pattern_password + - !!str ^.{3,}$ + - "pattern_password" ### user_delete() delete: @@ -146,9 +146,7 @@ user: username: help: Username to delete extra: - pattern: - - '^[a-z0-9_]+$' - - pattern_username + pattern: *pattern_username --purge: action: store_true @@ -164,33 +162,27 @@ user: -f: full: --firstname extra: - pattern: - - "^([^\\W\\d_]{2,30}[ ,.']{0,3})+$" - - pattern_firstname + pattern: *pattern_firstname -l: full: --lastname extra: - pattern: - - "^([^\\W\\d_]{2,30}[ ,.']{0,3})+$" - - pattern_lastname + pattern: *pattern_lastname -m: full: --mail extra: - pattern: - - '^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,6}$' - - pattern_email + pattern: *pattern_email -p: full: --change-password help: New password to set metavar: PASSWORD extra: - pattern: - - '^.{3,}$' - - pattern_password + pattern: *pattern_password --add-mailforward: help: Mailforward addresses to add nargs: "*" metavar: MAIL + extra: + pattern: *pattern_email --remove-mailforward: help: Mailforward addresses to remove nargs: "*" @@ -199,6 +191,8 @@ user: help: Mail aliases to add nargs: "*" metavar: MAIL + extra: + pattern: *pattern_email --remove-mailalias: help: Mail aliases to remove nargs: "*" @@ -253,9 +247,9 @@ domain: domain: help: Domain name to add extra: - pattern: - - '^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+[a-z]{2,}$' - - pattern_domain + pattern: &pattern_domain + - !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+[a-z]{2,}$ + - "pattern_domain" -d: full: --dyndns help: Subscribe to the DynDNS service @@ -271,9 +265,7 @@ domain: domain: help: Domain to delete extra: - pattern: - - '^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+[a-z]{2,}$' - - pattern_domain + pattern: *pattern_domain ### domain_info() # info: @@ -307,9 +299,9 @@ app: full: --name help: Name of the list (default fapp) extra: - pattern: - - '^[a-z0-9_]+$' - - pattern_listname + pattern: &pattern_listname + - !!str ^[a-z0-9_]+$ + - "pattern_listname" ### app_listlists() listlists: @@ -325,9 +317,7 @@ app: help: Name of the list to remove extra: ask: ask_list_to_remove - pattern: - - '^[a-z0-9_]+$' - - pattern_listname + pattern: *pattern_listname ### app_list() list: @@ -376,9 +366,7 @@ app: full: --user help: Allowed app map for a user extra: - pattern: - - '^[a-z0-9_]+$' - - pattern_username + pattern: *pattern_username ### app_install() install: @@ -454,9 +442,9 @@ app: port: help: Port to check extra: - pattern: - - '^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$' - - pattern_port + pattern: &pattern_port + - !!str ^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$ + - "pattern_port" ### app_checkurl() checkurl: @@ -575,9 +563,9 @@ backup: full: --name help: Name of the backup archive extra: - pattern: - - '^[\w\-\.]{1,30}(? Wed, 24 Dec 2014 17:04:29 +0100 + +moulinette-yunohost (2.0-rc~megusta32) test; urgency=low + + * Test build: [enh] Replace udiskie by udisks-glue + + -- Adrien Beudin Fri, 31 Oct 2014 19:36:18 +0100 + +moulinette-yunohost (2.0-rc~megusta31) test; urgency=low + + * Test build: [enh] Working backup and restore + + -- Adrien Beudin Sun, 26 Oct 2014 00:50:34 +0200 + +moulinette-yunohost (2.0-rc~megusta30) test; urgency=low + + * Test build: Fixes + + -- Adrien Beudin Sun, 26 Oct 2014 00:16:21 +0200 + +moulinette-yunohost (2.0-rc~megusta29) test; urgency=low + + * Test build: Restore function WIP + + -- Adrien Beudin Sat, 25 Oct 2014 23:23:25 +0200 + +moulinette-yunohost (2.0-rc~megusta28) test; urgency=low + + * Test build: typo + + -- Adrien Beudin Sat, 25 Oct 2014 20:41:10 +0200 + +moulinette-yunohost (2.0-rc~megusta27) test; urgency=low + + * Test build: typo + + -- Adrien Beudin Sat, 25 Oct 2014 20:09:26 +0200 + +moulinette-yunohost (2.0-rc~megusta26) test; urgency=low + + * Test build: typo + + -- Adrien Beudin Sat, 25 Oct 2014 19:38:54 +0200 + +moulinette-yunohost (2.0-rc~megusta25) test; urgency=low + + * Test build: Backup / restore WIP + + -- Adrien Beudin Sat, 25 Oct 2014 18:58:03 +0200 + +moulinette-yunohost (2.0-rc~megusta24) test; urgency=low + + * Test build: [enh] add firewall init script + + -- Adrien Beudin Tue, 16 Sep 2014 14:29:10 +0200 + +moulinette-yunohost (2.0-rc~megusta23) test; urgency=low + + * Test build: [enh] Add avahi daemon + + -- Adrien Beudin Tue, 16 Sep 2014 09:43:51 +0200 + +moulinette-yunohost (2.0-rc~megusta22) megusta; urgency=low + + * Production build: Bump version + + -- Adrien Beudin Thu, 31 Jul 2014 12:31:32 +0200 + +moulinette-yunohost (2.0-rc~megusta21) megusta; urgency=low + + * Production build: Bump version + + -- Adrien Beudin Thu, 31 Jul 2014 12:09:57 +0200 + +moulinette-yunohost (2.0-rc~megusta20) test; urgency=low + + * Test build: Update from git 31ef39e4e + + -- Adrien Beudin Mon, 28 Jul 2014 18:09:50 +0200 + +moulinette-yunohost (2.0-rc~megusta19) megusta; urgency=low + + * Production build: bump version + + -- Adrien Beudin Mon, 21 Jul 2014 16:23:15 +0200 + +moulinette-yunohost (2.0-rc~megusta18) megusta; urgency=low + + * Production build: Fix upgrade and various fixes + + -- Adrien Beudin Mon, 21 Jul 2014 16:16:57 +0200 + +moulinette-yunohost (2.0-rc~megusta17) test; urgency=low + + * Test build: Various fixes + + -- Adrien Beudin Mon, 21 Jul 2014 16:10:40 +0200 + +moulinette-yunohost (2.0-rc~megusta16) test; urgency=low + + * Test build: Update from git fed3e6f67 + + -- Adrien Beudin Fri, 18 Jul 2014 18:37:44 +0200 + +moulinette-yunohost (2.0-rc~megusta15) megusta; urgency=low + + * Production build: Update from git 496b4910159d + + -- Adrien Beudin Tue, 01 Jul 2014 19:08:29 +0200 + +moulinette-yunohost (2.0-rc~megusta14) test; urgency=low + + * Test build: Update from git 496b4910159d + + -- Adrien Beudin Tue, 01 Jul 2014 19:01:33 +0200 + +moulinette-yunohost (2.0-rc~megusta13) test; urgency=low + + * Test build: [fix] Init script + + -- Adrien Beudin Mon, 30 Jun 2014 17:52:52 +0200 + +moulinette-yunohost (2.0-rc~megusta12) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sat, 28 Jun 2014 15:07:05 +0200 + +moulinette-yunohost (2.0-rc~megusta11) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sat, 28 Jun 2014 10:59:30 +0200 + +moulinette-yunohost (2.0-rc~megusta10) test; urgency=low + + * Test build: [fix] Properly separate upnp and firewall + + -- Adrien Beudin Thu, 26 Jun 2014 12:40:47 +0200 + +moulinette-yunohost (2.0-rc~megusta9) test; urgency=low + + * Test build: [fix] API init script + + -- Adrien Beudin Wed, 25 Jun 2014 22:31:51 +0200 + +moulinette-yunohost (2.0-rc~megusta8) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Tue, 24 Jun 2014 13:56:53 +0200 + +moulinette-yunohost (2.0-rc~megusta7) megusta; urgency=low + + * Production build: [fix] copy firewall.yml file + + -- Adrien Beudin Sat, 14 Jun 2014 13:42:34 +0200 + +moulinette-yunohost (2.0-rc~megusta6) megusta; urgency=low + + * Production build: [fix] Wrong translation key in app module + + -- Adrien Beudin Thu, 12 Jun 2014 19:13:54 +0200 + +moulinette-yunohost (2.0-rc~megusta5) test; urgency=low + + * Test build: [fix] Add --no-websocket option to yunohost-api when + gevent segfault + + -- Adrien Beudin Thu, 12 Jun 2014 09:51:56 +0200 + +moulinette-yunohost (2.0-rc~megusta4) test; urgency=low + + * Test build: [fix] Add --no-websocket option to yunohost-api when + gevent segfault + + -- Adrien Beudin Thu, 12 Jun 2014 09:43:13 +0200 + +moulinette-yunohost (2.0-rc~megusta3) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Thu, 12 Jun 2014 09:28:32 +0200 + +moulinette-yunohost (2.0-rc~megusta2) megusta; urgency=low + + * Production build: Bump version + + -- Adrien Beudin Mon, 09 Jun 2014 01:43:09 +0200 + +moulinette-yunohost (2.0-rc~megusta1) test; urgency=low + + * Bump version + + -- Adrien Beudin Mon, 09 Jun 2014 00:49:09 +0200 + +moulinette-yunohost (2.0~megusta44) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 09 Jun 2014 00:49:09 +0200 + +moulinette-yunohost (2.0~megusta43) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Fri, 06 Jun 2014 12:24:33 +0200 + +moulinette-yunohost (2.0~megusta42) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 02 Jun 2014 22:10:12 +0200 + +moulinette-yunohost (2.0~megusta41) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 02 Jun 2014 11:29:54 +0200 + +moulinette-yunohost (2.0~megusta40) test; urgency=low + + * Test build: [fix] Remove --no-ldap argument while fetching applist + + -- Adrien Beudin Sun, 01 Jun 2014 21:44:08 +0200 + +moulinette-yunohost (2.0~megusta39) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sat, 31 May 2014 11:37:40 +0200 + +moulinette-yunohost (2.0~megusta38) test; urgency=low + + * Test build: [fix] Move udsikie init script + + -- Adrien Beudin Fri, 30 May 2014 13:37:38 +0200 + +moulinette-yunohost (2.0~megusta37) test; urgency=low + + * Test build: [fix] dependencies + + -- Adrien Beudin Thu, 29 May 2014 21:34:45 +0200 + +moulinette-yunohost (2.0~megusta36) test; urgency=low + + * Test build: [fix] dependencies + + -- Adrien Beudin Thu, 29 May 2014 21:21:52 +0200 + +moulinette-yunohost (2.0~megusta35) test; urgency=low + + * Test build: [fix] Install udiskie via pip + + -- Adrien Beudin Thu, 29 May 2014 10:48:21 +0200 + +moulinette-yunohost (2.0~megusta34) test; urgency=low + + * Test build: [fix] Install udiskie via pip + + -- Adrien Beudin Thu, 29 May 2014 10:20:24 +0200 + +moulinette-yunohost (2.0~megusta33) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Wed, 28 May 2014 21:05:27 +0200 + +moulinette-yunohost (2.0~megusta32) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Wed, 28 May 2014 15:38:24 +0200 + +moulinette-yunohost (2.0~megusta31) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Tue, 27 May 2014 14:44:24 +0200 + +moulinette-yunohost (2.0~megusta30) test; urgency=low + + * Test build: [fix] Reload SSOwat conf at postinst + + -- Adrien Beudin Tue, 27 May 2014 13:54:53 +0200 + +moulinette-yunohost (2.0~megusta29) test; urgency=low + + * Test build: [fix] Reload SSOwat conf at postinst + + -- Adrien Beudin Tue, 27 May 2014 13:51:08 +0200 + +moulinette-yunohost (2.0~megusta28) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Tue, 27 May 2014 12:44:06 +0200 + +moulinette-yunohost (2.0~megusta27) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 26 May 2014 13:40:28 +0200 + +moulinette-yunohost (2.0~megusta26) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sat, 24 May 2014 21:33:08 +0200 + +moulinette-yunohost (2.0~megusta25) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 19 May 2014 17:29:32 +0200 + +moulinette-yunohost (2.0~megusta24) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 19 May 2014 17:28:57 +0200 + +moulinette-yunohost (2.0~megusta23) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 19 May 2014 13:06:04 +0200 + +moulinette-yunohost (2.0~megusta22) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 19 May 2014 10:50:53 +0200 + +moulinette-yunohost (2.0~megusta21) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 19 May 2014 10:38:27 +0200 + +moulinette-yunohost (2.0~megusta20) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Mon, 19 May 2014 10:34:23 +0200 + +moulinette-yunohost (2.0~megusta19) test; urgency=low + + * Test build: [enh] Move init script in the moulinette-yunohost + package + + -- Adrien Beudin Sun, 18 May 2014 15:15:23 +0200 + +moulinette-yunohost (2.0~megusta18) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sun, 18 May 2014 12:11:17 +0200 + +moulinette-yunohost (2.0~megusta17) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sun, 18 May 2014 11:57:39 +0200 + +moulinette-yunohost (2.0~megusta16) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sun, 18 May 2014 11:14:48 +0200 + +moulinette-yunohost (2.0~megusta15) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sat, 17 May 2014 22:30:18 +0200 + +moulinette-yunohost (2.0~megusta14) test; urgency=low + + * Test build: [fix] Check if firewall.yml is old + + -- Adrien Beudin Sat, 17 May 2014 22:21:48 +0200 + +moulinette-yunohost (2.0~megusta13) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sat, 17 May 2014 22:10:52 +0200 + +moulinette-yunohost (2.0~megusta12) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sat, 17 May 2014 21:58:31 +0200 + +moulinette-yunohost (2.0~megusta11) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sat, 17 May 2014 00:35:13 +0200 + +moulinette-yunohost (2.0~megusta10) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Sat, 17 May 2014 00:16:12 +0200 + +moulinette-yunohost (2.0~megusta9) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Fri, 16 May 2014 23:19:51 +0200 + +moulinette-yunohost (2.0~megusta8) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Fri, 16 May 2014 21:46:50 +0200 + +moulinette-yunohost (2.0~megusta7) test; urgency=low + + * Test build: Remove cache after upgrade + + -- Adrien Beudin Fri, 16 May 2014 20:57:45 +0200 + +moulinette-yunohost (2.0~megusta6) test; urgency=low + + * Test build: Update from git + + -- Adrien Beudin Fri, 16 May 2014 20:05:08 +0200 + +moulinette-yunohost (2.0~megusta5) test; urgency=low + + * Test build: Update init script + + -- Adrien Beudin Fri, 16 May 2014 18:23:14 +0200 + +moulinette-yunohost (2.0~megusta4) test; urgency=low + + * Test build: actionSSSSS map + + -- Adrien Beudin Fri, 16 May 2014 18:05:45 +0200 + +moulinette-yunohost (2.0~megusta3) test; urgency=low + + * Test build: actionSSSSS map + + -- Adrien Beudin Fri, 16 May 2014 17:31:04 +0200 + +moulinette-yunohost (2.0~megusta2) test; urgency=low + + * Test build: Bump version + + -- Adrien Beudin Fri, 16 May 2014 16:28:45 +0200 + +moulinette-yunohost (2.0~megusta1) test; urgency=low + + * Test build: Add moulinette-yunohost package + + -- Adrien Beudin Fri, 16 May 2014 16:05:31 +0200 + +moulinette-yunohost (1.0~megusta1) megusta; urgency=low + + * Init + + -- Adrien Beudin Thu, 15 May 2014 13:16:03 +0200 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 000000000..ec635144f --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 000000000..2e7f0a4a3 --- /dev/null +++ b/debian/control @@ -0,0 +1,21 @@ +Source: moulinette-yunohost +Section: net +Priority: extra +Maintainer: Jérôme Lebleu +Build-Depends: debhelper (>=8.0.0) +Standards-Version: 3.9.4 +Homepage: https://yunohost.org/ + +Package: moulinette-yunohost +Architecture: all +Depends: moulinette, + python-psutil, + python-requests, + glances, + python-pip, + rubygems, + pyminiupnpc, + dnsutils +Description: YunoHost Python scripts + Python functions to manage a YunoHost instance + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/debian/install b/debian/install new file mode 100644 index 000000000..405448e5d --- /dev/null +++ b/debian/install @@ -0,0 +1,6 @@ +bin/* /usr/bin/ +data/actionsmap/* /usr/share/moulinette/actionsmap/ +data/hooks/* /usr/share/yunohost/hooks/ +data/other/* /usr/share/yunohost/yunohost-config/moulinette/ +lib/yunohost/*.py /usr/lib/moulinette/yunohost/ +locales/* /usr/lib/moulinette/yunohost/locales/ diff --git a/debian/moulinette-yunohost.yunohost-api.init b/debian/moulinette-yunohost.yunohost-api.init new file mode 100755 index 000000000..11c92b747 --- /dev/null +++ b/debian/moulinette-yunohost.yunohost-api.init @@ -0,0 +1,74 @@ +#! /bin/bash +### BEGIN INIT INFO +# Provides: yunohost-api +# Required-Start: $local_fs $remote_fs $network $syslog +# Required-Stop: $local_fs $remote_fs $network $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start/stop YunoHost API +### END INIT INFO + +DAEMON=/usr/bin/yunohost-api +DAEMON_OPTS="" + +test -x $DAEMON || exit 0 + +. /lib/lsb/init-functions + +logger "YunoHost API: Start script executed" + +case "$1" in + start) + logger "YunoHost API: Starting" + log_daemon_msg "Starting API: YunoHost" + if [[ -f /etc/nginx/conf.d/openresty.conf ]]; + then + DAEMON_OPTS="--no-websocket" + fi + start-stop-daemon --start --background --pidfile /var/run/yunohost-api.pid --make-pidfile \ + --exec /bin/bash -- -c "$DAEMON $DAEMON_OPTS >> /var/log/yunohost.log 2>&1" + log_end_msg $? + ;; + stop) + logger "YunoHost API: Stopping" + log_daemon_msg "Stopping API: YunoHost" + if [ -f /var/run/yunohost-api.pid ]; then + kill `cat /var/run/yunohost-api.pid` > /dev/null 2>&1 + rm -f /var/run/yunohost-api.pid + fi + kill `ps aux | grep 'python /usr/bin/yunohost-api' | grep -v grep | awk '{print $2}'` > /dev/null 2>&1 + kill `ps aux | grep 'yunohost-api' | grep -v grep | grep -v stop | awk '{print $2}'` > /dev/null 2>&1 + log_end_msg 0 + ;; + restart) + logger "YunoHost API: Restarting" + log_daemon_msg "Restarting API: YunoHost" + if [ -f /var/run/yunohost-api.pid ]; then + kill `cat /var/run/yunohost-api.pid` > /dev/null 2>&1 + rm -f /var/run/yunohost-api.pid + fi + kill `ps aux | grep 'python /usr/bin/yunohost-api' | grep -v grep | awk '{print $2}'` > /dev/null 2>&1 + kill `ps aux | grep 'yunohost-api' | grep -v grep | grep -v restart | awk '{print $2}'` > /dev/null 2>&1 + kill `ps aux | grep 'yunohost.tac' | grep -v grep | awk '{print $2}'` > /dev/null 2>&1 + if [[ -f /etc/nginx/conf.d/openresty.conf ]]; + then + DAEMON_OPTS="--no-websocket" + fi + start-stop-daemon --start --background --pidfile /var/run/yunohost-api.pid --make-pidfile \ + --exec /bin/bash -- -c "$DAEMON $DAEMON_OPTS >> /var/log/yunohost.log 2>&1" + log_end_msg $? + ;; + status) + logger "YunoHost API: Running" + log_daemon_msg "YunoHost API: Running" + cat /var/run/yunohost-api.pid > /dev/null 2>&1 + log_end_msg $? + ;; + *) + logger "YunoHost API: Invalid usage" + echo "Usage: /etc/init.d/yunohost-api {start|stop|restart|status}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 000000000..2ad815792 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,56 @@ +#!/bin/bash + +TMP=/usr/share/yunohost/yunohost-config/moulinette + +if [ ! -d /etc/yunohost ]; +then + mkdir -p /etc/yunohost +fi + +# Allow users to access /media directory +if [ ! -d /etc/skel/media ]; +then + mkdir -p /media + ln -s /media /etc/skel/ +fi + +#Firewall +grep -q "UPNP:" /etc/yunohost/firewall.yml > /dev/null 2>&1 +if [[ $? -eq 0 ]] || [ ! -f /etc/yunohost/firewall.yml ]; +then + cp $TMP/firewall.yml /etc/yunohost/ +fi + +# App fetchlist +if [ -f /etc/cron.d/yunohost-applist-yunohost ]; +then + sed -i "s/--no-ldap //g" /etc/cron.d/yunohost-applist-yunohost +fi + +# Service list +if [ ! -f /etc/yunohost/services.yml ]; +then + cp $TMP/services.yml /etc/yunohost/ +fi + +# Stop old API +ps aux | grep "yunohost.tac" | grep -qv grep +if [[ $? -eq 0 ]]; +then + killall twistd +fi + +rm -rf /var/cache/moulinette/* +update-rc.d yunohost-api defaults +service yunohost-api restart + +# Reload SSOwat conf if obsolete +if [ -f /etc/yunohost/installed ]; +then + yunohost firewall upnp | grep -qi "true" + if [[ $? -eq 0 ]]; + then + yunohost firewall upnp enable + fi + yunohost app ssowatconf +fi diff --git a/debian/preinst b/debian/preinst new file mode 100644 index 000000000..43850db1a --- /dev/null +++ b/debian/preinst @@ -0,0 +1,11 @@ +#!/bin/sh +set -e + +if [ -f /etc/init.d/yunohost-api ]; then + service yunohost-api stop +# nc -zv 127.0.0.1 6787 < /dev/null 2> /dev/null +# if [[ ! $? -eq 0 ]]; +# then +# exit 1 +# fi +fi diff --git a/debian/rules b/debian/rules new file mode 100755 index 000000000..cb4a4ccfc --- /dev/null +++ b/debian/rules @@ -0,0 +1,11 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ + +override_dh_installinit: + dh_installinit --name=yunohost-api diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 000000000..89ae9db8f --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/firewall.py b/firewall.py deleted file mode 100644 index 08b63b3bc..000000000 --- a/firewall.py +++ /dev/null @@ -1,311 +0,0 @@ -# -*- coding: utf-8 -*- - -""" License - - Copyright (C) 2013 YunoHost - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program; if not, see http://www.gnu.org/licenses - -""" - -""" yunohost_firewall.py - - Manage firewall rules -""" -import os -import sys -import yaml -import errno -try: - import miniupnpc -except ImportError: - sys.stderr.write('Error: Yunohost CLI Require miniupnpc lib\n') - sys.exit(1) - -from moulinette.core import MoulinetteError - -""" Search the ssh port in ssh config file - If we don't find the ssh port we define 22""" - -try: - with open('/etc/ssh/sshd_config') as ssh_config_file: - for line in ssh_config_file: - line0 = line.split(" ")[0] - - if line0 == 'Port': - ssh_port = line.split(' ')[1] - ssh_port = ssh_port.rstrip('\n\r') - - ssh_config_file.close() - - if ssh_port == '': - ssh_port = '22' - -except: - ssh_port = '22' - -ssh_port = int(ssh_port) - -def firewall_allow(port=None, protocol=['TCP'], ipv6=False, no_upnp=False): - """ - Allow connection port/protocol - - Keyword argument: - port -- Port to open - protocol -- Protocol associated with port - ipv6 -- ipv6 - no_upnp -- Do not request for uPnP - - """ - port = int(port) - ipv = "ipv4" - if isinstance(protocol, list): - protocols = protocol - else: - protocols = [protocol] - protocol = protocols[0] - - firewall = firewall_list(raw=True) - - upnp = not no_upnp and firewall['uPnP']['enabled'] - - if ipv6: - ipv = "ipv6" - - if protocol == "Both": - protocols = ['UDP', 'TCP'] - - for protocol in protocols: - if upnp and port not in firewall['uPnP'][protocol]: - firewall['uPnP'][protocol].append(port) - if port not in firewall[ipv][protocol]: - firewall[ipv][protocol].append(port) - else: - msignals.display(m18n.n('port_already_opened', port), 'warning') - - with open('/etc/yunohost/firewall.yml', 'w') as f: - yaml.safe_dump(firewall, f, default_flow_style=False) - - return firewall_reload() - - -def firewall_disallow(port=None, protocol=['TCP'], ipv6=False): - """ - Allow connection port/protocol - - Keyword argument: - port -- Port to open - protocol -- Protocol associated with port - ipv6 -- ipv6 - - """ - port = int(port) - ipv = "ipv4" - if isinstance(protocol, list): - protocols = protocol - else: - protocols = [protocol] - protocol = protocols[0] - - firewall = firewall_list(raw=True) - - if ipv6: - ipv = "ipv6" - - if protocol == "Both": - protocols = ['UDP', 'TCP'] - - for protocol in protocols: - if port in firewall['uPnP'][protocol]: - firewall['uPnP'][protocol].remove(port) - if port in firewall[ipv][protocol]: - firewall[ipv][protocol].remove(port) - else: - msignals.display(m18n.n('port_already_closed', port), 'warning') - - with open('/etc/yunohost/firewall.yml', 'w') as f: - yaml.safe_dump(firewall, f, default_flow_style=False) - - return firewall_reload() - - -def firewall_list(raw=False): - """ - List all firewall rules - - Keyword argument: - raw -- Return the complete YAML dict - - """ - with open('/etc/yunohost/firewall.yml') as f: - firewall = yaml.load(f) - - if raw: - return firewall - else: - return { "openned_ports": firewall['ipv4']['TCP'] } - - -def firewall_reload(): - """ - Reload all firewall rules - - - """ - from yunohost.hook import hook_callback - - firewall = firewall_list(raw=True) - upnp = firewall['uPnP']['enabled'] - - # IPv4 - if os.system("iptables -P INPUT ACCEPT") != 0: - raise MoulinetteError(errno.ESRCH, m18n.n('iptables_unavailable')) - if upnp: - firewall_upnp(action=['reload']) - - os.system("iptables -F") - os.system("iptables -X") - os.system("iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT") - - if ssh_port not in firewall['ipv4']['TCP']: - firewall_allow(ssh_port) - - # Loop - for protocol in ['TCP', 'UDP']: - for port in firewall['ipv4'][protocol]: - os.system("iptables -A INPUT -p %s --dport %d -j ACCEPT" % (protocol, port)) - - hook_callback('post_iptable_rules', - args=[upnp, os.path.exists("/proc/net/if_inet6")]) - - os.system("iptables -A INPUT -i lo -j ACCEPT") - os.system("iptables -A INPUT -p icmp -j ACCEPT") - os.system("iptables -P INPUT DROP") - - # IPv6 - if os.path.exists("/proc/net/if_inet6"): - os.system("ip6tables -P INPUT ACCEPT") - os.system("ip6tables -F") - os.system("ip6tables -X") - os.system("ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT") - - if ssh_port not in firewall['ipv6']['TCP']: - firewall_allow(ssh_port, ipv6=True) - - # Loop v6 - for protocol in ['TCP', 'UDP']: - for port in firewall['ipv6'][protocol]: - os.system("ip6tables -A INPUT -p %s --dport %d -j ACCEPT" % (protocol, port)) - - os.system("ip6tables -A INPUT -i lo -j ACCEPT") - os.system("ip6tables -A INPUT -p icmpv6 -j ACCEPT") - os.system("ip6tables -P INPUT DROP") - - os.system("service fail2ban restart") - msignals.display(m18n.n('firewall_reloaded'), 'success') - - return firewall_list() - - -def firewall_upnp(action=None): - """ - Add uPnP cron and enable uPnP in firewall.yml, or the opposite. - - Keyword argument: - action -- enable/disable/reload - - """ - firewall = firewall_list(raw=True) - - if action: - action = action[0] - - if action == 'enable': - firewall['uPnP']['enabled'] = True - - with open('/etc/cron.d/yunohost-firewall', 'w+') as f: - f.write('PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ - \n*/50 * * * * root yunohost firewall upnp reload >>/dev/null') - - msignals.display(m18n.n('upnp_enabled'), 'success') - - if action == 'disable': - firewall['uPnP']['enabled'] = False - - try: - upnpc = miniupnpc.UPnP() - upnpc.discoverdelay = 3000 - if upnpc.discover() == 1: - upnpc.selectigd() - for protocol in ['TCP', 'UDP']: - for port in firewall['uPnP'][protocol]: - if upnpc.getspecificportmapping(port, protocol): - try: upnpc.deleteportmapping(port, protocol) - except: pass - except: pass - - - try: os.remove('/etc/cron.d/yunohost-firewall') - except: pass - - msignals.display(m18n.n('upnp_disabled'), 'success') - - if action == 'reload': - upnp = firewall['uPnP']['enabled'] - - if upnp: - try: - upnpc = miniupnpc.UPnP() - upnpc.discoverdelay = 3000 - if upnpc.discover() == 1: - upnpc.selectigd() - for protocol in ['TCP', 'UDP']: - for port in firewall['uPnP'][protocol]: - if upnpc.getspecificportmapping(port, protocol): - try: upnpc.deleteportmapping(port, protocol) - except: pass - upnpc.addportmapping(port, protocol, upnpc.lanaddr, port, 'yunohost firewall : port %d' % port, '') - else: - raise MoulinetteError(errno.ENXIO, m18n.n('upnp_dev_not_found')) - except: - msignals.display(m18n.n('upnp_port_open_failed'), 'warning') - - if action: - os.system("cp /etc/yunohost/firewall.yml /etc/yunohost/firewall.yml.old") - with open('/etc/yunohost/firewall.yml', 'w') as f: - yaml.safe_dump(firewall, f, default_flow_style=False) - - return { "enabled": firewall['uPnP']['enabled'] } - - -def firewall_stop(): - """ - Stop iptables and ip6tables - - - """ - - if os.system("iptables -P INPUT ACCEPT") != 0: - raise MoulinetteError(errno.ESRCH, m18n.n('iptables_unavailable')) - - os.system("iptables -F") - os.system("iptables -X") - - if os.path.exists("/proc/net/if_inet6"): - os.system("ip6tables -P INPUT ACCEPT") - os.system("ip6tables -F") - os.system("ip6tables -X") - - if os.path.exists("/etc/cron.d/yunohost-firewall"): - firewall_upnp('disable') diff --git a/__init__.py b/lib/yunohost/__init__.py similarity index 100% rename from __init__.py rename to lib/yunohost/__init__.py diff --git a/app.py b/lib/yunohost/app.py similarity index 100% rename from app.py rename to lib/yunohost/app.py diff --git a/backup.py b/lib/yunohost/backup.py similarity index 100% rename from backup.py rename to lib/yunohost/backup.py diff --git a/domain.py b/lib/yunohost/domain.py similarity index 100% rename from domain.py rename to lib/yunohost/domain.py diff --git a/dyndns.py b/lib/yunohost/dyndns.py similarity index 100% rename from dyndns.py rename to lib/yunohost/dyndns.py diff --git a/lib/yunohost/firewall.py b/lib/yunohost/firewall.py new file mode 100644 index 000000000..7e114f4a1 --- /dev/null +++ b/lib/yunohost/firewall.py @@ -0,0 +1,446 @@ +# -*- coding: utf-8 -*- + +""" License + + Copyright (C) 2013 YunoHost + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, see http://www.gnu.org/licenses + +""" + +""" yunohost_firewall.py + + Manage firewall rules +""" +import os +import sys +import yaml +import errno +try: + import miniupnpc +except ImportError: + sys.stderr.write('Error: Yunohost CLI Require miniupnpc lib\n') + sys.exit(1) + +from moulinette.core import MoulinetteError +from moulinette.utils import process +from moulinette.utils.log import getActionLogger +from moulinette.utils.text import prependlines + +firewall_file = '/etc/yunohost/firewall.yml' +upnp_cron_job = '/etc/cron.d/yunohost-firewall-upnp' + +logger = getActionLogger('yunohost.firewall') + + +def firewall_allow(port, protocol='TCP', ipv4_only=False, ipv6_only=False, + no_upnp=False, no_reload=False): + """ + Allow connections on a port + + Keyword arguments: + port -- Port or range of ports to open + protocol -- Protocol type to allow (default: TCP) + ipv4_only -- Only add a rule for IPv4 connections + ipv6_only -- Only add a rule for IPv6 connections + no_upnp -- Do not add forwarding of this port with UPnP + no_reload -- Do not reload firewall rules + + """ + firewall = firewall_list(raw=True) + + # Validate port + if not isinstance(port, int) and ':' not in port: + port = int(port) + + # Validate protocols + protocols = ['TCP', 'UDP'] + if protocol != 'Both' and protocol in protocols: + protocols = [protocol,] + + # Validate IP versions + ipvs = ['ipv4', 'ipv6'] + if ipv4_only and not ipv6_only: + ipvs = ['ipv4',] + elif ipv6_only and not ipv4_only: + ipvs = ['ipv6',] + + for p in protocols: + # Iterate over IP versions to add port + for i in ipvs: + if port not in firewall[i][p]: + firewall[i][p].append(port) + else: + ipv = "IPv%s" % i[3] + msignals.display(m18n.n('port_already_opened', port, ipv), + 'warning') + # Add port forwarding with UPnP + if not no_upnp and port not in firewall['uPnP'][p]: + firewall['uPnP'][p].append(port) + + # Update and reload firewall + _update_firewall_file(firewall) + if not no_reload: + return firewall_reload() + + +def firewall_disallow(port, protocol='TCP', ipv4_only=False, ipv6_only=False, + upnp_only=False, no_reload=False): + """ + Disallow connections on a port + + Keyword arguments: + port -- Port or range of ports to close + protocol -- Protocol type to disallow (default: TCP) + ipv4_only -- Only remove the rule for IPv4 connections + ipv6_only -- Only remove the rule for IPv6 connections + upnp_only -- Only remove forwarding of this port with UPnP + no_reload -- Do not reload firewall rules + + """ + firewall = firewall_list(raw=True) + + # Validate port + if ':' not in port: + port = int(port) + + # Validate protocols + protocols = ['TCP', 'UDP'] + if protocol != 'Both' and protocol in protocols: + protocols = [protocol,] + + # Validate IP versions and UPnP + ipvs = ['ipv4', 'ipv6'] + upnp = True + if ipv4_only and ipv6_only: + upnp = True # automatically disallow UPnP + elif ipv4_only: + ipvs = ['ipv4',] + upnp = upnp_only + elif ipv6_only: + ipvs = ['ipv6',] + upnp = upnp_only + elif upnp_only: + ipvs = [] + + for p in protocols: + # Iterate over IP versions to remove port + for i in ipvs: + if port in firewall[i][p]: + firewall[i][p].remove(port) + else: + ipv = "IPv%s" % i[3] + msignals.display(m18n.n('port_already_closed', port, ipv), + 'warning') + # Remove port forwarding with UPnP + if upnp and port in firewall['uPnP'][p]: + firewall['uPnP'][p].remove(port) + + # Update and reload firewall + _update_firewall_file(firewall) + if not no_reload: + return firewall_reload() + + +def firewall_list(raw=False, by_ip_version=False, list_forwarded=False): + """ + List all firewall rules + + Keyword arguments: + raw -- Return the complete YAML dict + by_ip_version -- List rules by IP version + list_forwarded -- List forwarded ports with UPnP + + """ + with open(firewall_file) as f: + firewall = yaml.load(f) + if raw: + return firewall + + # Retrieve all ports for IPv4 and IPv6 + ports = {} + for i in ['ipv4', 'ipv6']: + f = firewall[i] + # Combine TCP and UDP ports + ports[i] = sorted(set(f['TCP']) | set(f['UDP'])) + + if not by_ip_version: + # Combine IPv4 and IPv6 ports + ports = sorted(set(ports['ipv4']) | set(ports['ipv6'])) + + # Format returned dict + ret = { "opened_ports": ports } + if list_forwarded: + # Combine TCP and UDP forwarded ports + ret['forwarded_ports'] = sorted( + set(firewall['uPnP']['TCP']) | set(firewall['uPnP']['UDP'])) + return ret + + +def firewall_reload(): + """ + Reload all firewall rules + + + """ + from yunohost.hook import hook_callback + + reloaded = False + errors = False + + # Check if SSH port is allowed + ssh_port = _get_ssh_port() + if ssh_port not in firewall_list()['opened_ports']: + firewall_allow(ssh_port, no_reload=True) + + # Retrieve firewall rules and UPnP status + firewall = firewall_list(raw=True) + upnp = firewall_upnp()['enabled'] + + # IPv4 + try: + process.check_output("iptables -L") + except process.CalledProcessError as e: + logger.info('iptables seems to be not available, it outputs:\n%s', + prependlines(e.output.rstrip(), '> ')) + msignals.display(m18n.n('iptables_unavailable'), 'info') + else: + rules = [ + "iptables -F", + "iptables -X", + "iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT", + ] + # Iterate over ports and add rule + for protocol in ['TCP', 'UDP']: + for port in firewall['ipv4'][protocol]: + rules.append("iptables -A INPUT -p %s --dport %s -j ACCEPT" \ + % (protocol, process.quote(str(port)))) + rules += [ + "iptables -A INPUT -i lo -j ACCEPT", + "iptables -A INPUT -p icmp -j ACCEPT", + "iptables -P INPUT DROP", + ] + + # Execute each rule + if process.check_commands(rules, callback=_on_rule_command_error): + errors = True + reloaded = True + + # IPv6 + try: + process.check_output("ip6tables -L") + except process.CalledProcessError as e: + logger.info('ip6tables seems to be not available, it outputs:\n%s', + prependlines(e.output.rstrip(), '> ')) + msignals.display(m18n.n('ip6tables_unavailable'), 'info') + else: + rules = [ + "ip6tables -F", + "ip6tables -X", + "ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT", + ] + # Iterate over ports and add rule + for protocol in ['TCP', 'UDP']: + for port in firewall['ipv6'][protocol]: + rules.append("ip6tables -A INPUT -p %s --dport %s -j ACCEPT" \ + % (protocol, process.quote(str(port)))) + rules += [ + "ip6tables -A INPUT -i lo -j ACCEPT", + "ip6tables -A INPUT -p icmpv6 -j ACCEPT", + "ip6tables -P INPUT DROP", + ] + + # Execute each rule + if process.check_commands(rules, callback=_on_rule_command_error): + errors = True + reloaded = True + + if not reloaded: + raise MoulinetteError(errno.ESRCH, m18n.n('firewall_reload_failed')) + + hook_callback('post_iptable_rules', + args=[upnp, os.path.exists("/proc/net/if_inet6")]) + + if upnp: + # Refresh port forwarding with UPnP + firewall_upnp(no_refresh=False) + + # TODO: Use service_restart + os.system("service fail2ban restart") + + if errors: + msignals.display(m18n.n('firewall_rules_cmd_failed'), 'warning') + else: + msignals.display(m18n.n('firewall_reloaded'), 'success') + return firewall_list() + + +def firewall_upnp(action='status', no_refresh=False): + """ + Manage port forwarding using UPnP + + Note: 'reload' action is deprecated and will be removed in the near + future. You should use 'status' instead - which retrieve UPnP status + and automatically refresh port forwarding if 'no_refresh' is False. + + Keyword argument: + action -- Action to perform + no_refresh -- Do not refresh port forwarding + + """ + firewall = firewall_list(raw=True) + enabled = firewall['uPnP']['enabled'] + + # Compatibility with previous version + if action == 'reload': + logger.warning("'reload' action is deprecated and will be removed") + try: + # Remove old cron job + os.remove('/etc/cron.d/yunohost-firewall') + except: pass + action = 'status' + no_refresh = False + + if action == 'status' and no_refresh: + # Only return current state + return { 'enabled': enabled } + elif action == 'enable' or (enabled and action == 'status'): + # Add cron job + with open(upnp_cron_job, 'w+') as f: + f.write('*/50 * * * * root ' + '/usr/bin/yunohost firewall upnp status >>/dev/null\n') + enabled = True + elif action == 'disable' or (not enabled and action == 'status'): + try: + # Remove cron job + os.remove(upnp_cron_job) + except: pass + enabled = False + if action == 'status': + no_refresh = True + else: + raise MoulinetteError(errno.EINVAL, m18n.n('action_invalid', action)) + + # Refresh port mapping using UPnP + if not no_refresh: + upnpc = miniupnpc.UPnP() + upnpc.discoverdelay = 3000 + + # Discover UPnP device(s) + logger.debug('discovering UPnP devices...') + nb_dev = upnpc.discover() + logger.debug('found %d UPnP device(s)', int(nb_dev)) + if nb_dev < 1: + msignals.display(m18n.n('upnp_dev_not_found'), 'error') + enabled = False + else: + try: + # Select UPnP device + upnpc.selectigd() + except: + logger.exception('unable to select UPnP device') + enabled = False + else: + # Iterate over ports + for protocol in ['TCP', 'UDP']: + for port in firewall['uPnP'][protocol]: + # Clean the mapping of this port + if upnpc.getspecificportmapping(port, protocol): + try: + upnpc.deleteportmapping(port, protocol) + except: pass + if not enabled: + continue + try: + # Add new port mapping + upnpc.addportmapping(port, protocol, upnpc.lanaddr, + port, 'yunohost firewall: port %d' % port, '') + except: + logger.exception('unable to add port %d using UPnP', + port) + enabled = False + + if enabled != firewall['uPnP']['enabled']: + firewall['uPnP']['enabled'] = enabled + + # Make a backup and update firewall file + os.system("cp {0} {0}.old".format(firewall_file)) + with open(firewall_file, 'w') as f: + yaml.safe_dump(firewall, f, default_flow_style=False) + + if not no_refresh: + # Display success message if needed + if action == 'enable' and enabled: + msignals.display(m18n.n('upnp_enabled'), 'success') + elif action == 'disable' and not enabled: + msignals.display(m18n.n('upnp_disabled'), 'success') + # Make sure to disable UPnP + elif action != 'disable' and not enabled: + firewall_upnp('disable', no_refresh=True) + + if action == 'enable' and not enabled: + raise MoulinetteError(errno.ENXIO, m18n.n('upnp_port_open_failed')) + return { 'enabled': enabled } + + +def firewall_stop(): + """ + Stop iptables and ip6tables + + + """ + + if os.system("iptables -P INPUT ACCEPT") != 0: + raise MoulinetteError(errno.ESRCH, m18n.n('iptables_unavailable')) + + os.system("iptables -F") + os.system("iptables -X") + + if os.path.exists("/proc/net/if_inet6"): + os.system("ip6tables -P INPUT ACCEPT") + os.system("ip6tables -F") + os.system("ip6tables -X") + + if os.path.exists(upnp_cron_job): + firewall_upnp('disable') + + +def _get_ssh_port(default=22): + """Return the SSH port to use + + Retrieve the SSH port from the sshd_config file or used the default + one if it's not defined. + """ + from moulinette.utils.text import searchf + try: + m = searchf(r'^Port[ \t]+([0-9]+)$', + '/etc/ssh/sshd_config', count=-1) + if m: + return int(m) + except: + pass + return default + +def _update_firewall_file(rules): + """Make a backup and write new rules to firewall file""" + os.system("cp {0} {0}.old".format(firewall_file)) + with open(firewall_file, 'w') as f: + yaml.safe_dump(rules, f, default_flow_style=False) + +def _on_rule_command_error(returncode, cmd, output): + """Callback for rules commands error""" + # Log error and continue commands execution + logger.error('"%s" returned non-zero exit status %d:\n%s', + cmd, returncode, prependlines(output.rstrip(), '> ')) + return True diff --git a/hook.py b/lib/yunohost/hook.py similarity index 99% rename from hook.py rename to lib/yunohost/hook.py index 4c7595de2..ccfd242b8 100644 --- a/hook.py +++ b/lib/yunohost/hook.py @@ -252,7 +252,7 @@ def hook_exec(file, args=None): args -- Arguments to pass to the script """ - from moulinette.helpers import NonBlockingStreamReader + from moulinette.utils.stream import NonBlockingStreamReader from yunohost.app import _value_for_locale if isinstance(args, list): diff --git a/monitor.py b/lib/yunohost/monitor.py similarity index 100% rename from monitor.py rename to lib/yunohost/monitor.py diff --git a/service.py b/lib/yunohost/service.py similarity index 100% rename from service.py rename to lib/yunohost/service.py diff --git a/tools.py b/lib/yunohost/tools.py similarity index 98% rename from tools.py rename to lib/yunohost/tools.py index 1e4fce2f4..fb09935e9 100644 --- a/tools.py +++ b/lib/yunohost/tools.py @@ -316,14 +316,11 @@ def tools_postinstall(domain, password, ignore_dyndns=False): app_ssowatconf(auth) # Change LDAP admin password - tools_adminpw(old_password='yunohost', new_password=password) + tools_adminpw(auth, password) - # Enable uPnP - firewall_upnp(action=['enable']) - try: - firewall_reload() - except MoulinetteError: - firewall_upnp(action=['disable']) + # Enable UPnP silently and reload firewall + firewall_upnp('enable', no_refresh=True) + firewall_reload() # Enable iptables at boot time os.system('update-rc.d yunohost-firewall defaults') diff --git a/user.py b/lib/yunohost/user.py similarity index 100% rename from user.py rename to lib/yunohost/user.py diff --git a/locales/en.json b/locales/en.json index ae3229f3f..c829c5d94 100644 --- a/locales/en.json +++ b/locales/en.json @@ -6,6 +6,7 @@ "installation_complete" : "Installation complete", "installation_failed" : "Installation failed", "unexpected_error" : "An unexpected error occured", + "action_invalid" : "Invalid action '{:s}'", "license_undefined" : "undefined", "no_appslist_found" : "No apps list found", @@ -67,15 +68,18 @@ "dyndns_cron_remove_failed" : "Unable to remove DynDNS cron job", "dyndns_cron_removed" : "DynDNS cron job successfully removed", - "port_available" : "Port {:d} is available", - "port_unavailable" : "Port {:d} is not available", - "port_already_opened" : "Port {:d} is already opened", - "port_already_closed" : "Port {:d} is already closed", + "port_available" : "Port {} is available", + "port_unavailable" : "Port {} is not available", + "port_already_opened" : "Port {} is already opened for {:s} connections", + "port_already_closed" : "Port {} is already closed for {:s} connections", "iptables_unavailable" : "You cannot play with iptables here. You are either in a container or your kernel does not support it.", - "upnp_dev_not_found" : "No uPnP device found", - "upnp_port_open_failed" : "Unable to open uPnP ports", - "upnp_enabled" : "uPnP successfully enabled", - "upnp_disabled" : "uPnP successfully disabled", + "ip6tables_unavailable" : "You cannot play with ip6tables here. You are either in a container or your kernel does not support it.", + "upnp_dev_not_found" : "No UPnP device found", + "upnp_port_open_failed" : "Unable to open UPnP ports", + "upnp_enabled" : "UPnP successfully enabled", + "upnp_disabled" : "UPnP successfully disabled", + "firewall_rules_cmd_failed" : "Some firewall rules commands have failed. For more information, see the log.", + "firewall_reload_failed" : "Unable to reload firewall", "firewall_reloaded" : "Firewall successfully reloaded", "hook_list_by_invalid" : "Invalid property to list hook by", @@ -180,6 +184,7 @@ "pattern_domain" : "Must be a valid domain name (e.g. my-domain.org)", "pattern_listname" : "Must be alphanumeric and underscore characters only", "pattern_port" : "Must be a valid port number (i.e. 0-65535)", + "pattern_port_or_range" : "Must be a valid port number (i.e. 0-65535) or range of ports (e.g. 100:200)", "pattern_backup_archive_name" : "Must be a valid filename with alphanumeric and -_. characters only", "format_datetime_short" : "%m/%d/%Y %I:%M %p" diff --git a/locales/fr.json b/locales/fr.json index 827ff34d1..43c74bba3 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -6,6 +6,7 @@ "installation_complete" : "Installation terminée", "installation_failed" : "Échec de l'installation", "unexpected_error" : "Une erreur inattendue est survenue", + "action_invalid" : "Action '{:s}' incorrecte", "license_undefined" : "indéfinie", "no_appslist_found" : "Aucune liste d'applications trouvée", @@ -67,15 +68,18 @@ "dyndns_cron_remove_failed" : "Impossible d'enlever la tâche cron pour DynDNS", "dyndns_cron_removed" : "Tâche cron pour DynDNS enlevée avec succès", - "port_available" : "Le port {:d} est disponible", - "port_unavailable" : "Le port {:d} est indisponible", - "port_already_opened" : "Le port {:d} est déjà ouvert", - "port_already_closed" : "Le port {:d} est déjà fermé", + "port_available" : "Le port {} est disponible", + "port_unavailable" : "Le port {} est indisponible", + "port_already_opened" : "Le port {} est déjà ouvert pour l'{:s}", + "port_already_closed" : "Le port {} est déjà fermé pour l'{:s}", "iptables_unavailable" : "Vous ne pouvez pas faire joujou avec iptables ici. Vous êtes sûrement dans un conteneur, autrement votre noyau ne le supporte pas.", - "upnp_dev_not_found" : "Aucun périphérique compatible uPnP trouvé", - "upnp_port_open_failed" : "Impossible d'ouvrir les ports avec uPnP", - "upnp_enabled" : "uPnP activé avec succès", - "upnp_disabled" : "uPnP désactivé avec succès", + "ip6tables_unavailable" : "Vous ne pouvez pas faire joujou avec ip6tables ici. Vous êtes sûrement dans un conteneur, autrement votre noyau ne le supporte pas.", + "upnp_dev_not_found" : "Aucun périphérique compatible UPnP trouvé", + "upnp_port_open_failed" : "Impossible d'ouvrir les ports avec UPnP", + "upnp_enabled" : "UPnP activé avec succès", + "upnp_disabled" : "UPnP désactivé avec succès", + "firewall_rules_cmd_failed" : "Des commandes de règles du pare-feu ont échoués. Plus d'informations sont disponibles dans le journal d'erreurs.", + "firewall_reload_failed" : "Impossible de recharger le pare-feu", "firewall_reloaded" : "Pare-feu rechargé avec succès", "hook_list_by_invalid" : "Propriété pour lister les scripts incorrecte", @@ -180,6 +184,7 @@ "pattern_domain" : "Doit être un nom de domaine valide (ex : mon-domaine.org)", "pattern_listname" : "Doit être composé uniquement de caractères alphanumérique et de tiret bas", "pattern_port" : "Doit être un numéro de port valide (0-65535)", + "pattern_port_or_range" : "Doit être un numéro de port valide (0-65535) ou une gamme de ports (ex : 100:200)", "pattern_backup_archive_name" : "Doit être un nom de fichier valide composé de caractères alphanumérique et -_. uniquement", "format_datetime_short" : "%d/%m/%Y %H:%M"