devel
Alexey Berezhok 1 year ago
commit 346a50856b

File diff suppressed because it is too large Load Diff

@ -0,0 +1,76 @@
# Hestia Control Panel - Contribution Guidelines
## Ways to contribute
- **Beta testing**:
- Download and install builds from the `beta` branch. If you encounter an issue with a beta build, file an issue report on [GitHub](https://www.github.com/hestiacp/hestiacp/issues).<br>
`v-update-sys-hestia-git hestiacp beta install` will install the latest beta build from our GitHub repository.
- **Code review and bug fixes**:
- Read over the code and if you notice errors (even spelling mistakes), submit a pull request with your changes.
- **New features**:
- Is there an awesome feature that you'd love to see included? Submit a pull request with your changes, and if approved your PR will be reviewed and merged for inclusion in an upcoming release. While our development team tries to accommodate all reasonable requests please remember that it does take time to develop, implement and test new features and as such we may not be able to fulfill all requests or may have to put a feature on backlog for a later date.
- **Translations**:
- If you are a non-English speaker and would like to improve the quality of the translations used in Hestia Control Panel's web interface, please go to [Hestia Translate](https://translate.hestiacp.com/projects/hestiacp/) to review the translations database. For more information please read [How to contribute with Translations](https://forum.hestiacp.com/t/how-to-contribute-with-translations/1664) on our forum. You can also open an issue report [GitHub](https://www.github.com/hestiacp/hestiacp/issues) highlighting the issue with the current translation so that it can be corrected.
- **Donations**:
- If you're not a developer but you still want to make a contribution to support Hestia Control Panel and our developers, you can make a donation to the Hestia Control Panel project to further its development (or if you'd just like to buy our developers a lunch, we'd appreciate that too). We currently accept donations through [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ST87LQH2CHGLA).
## Development Guidelines
Additional information on how to contribute to Hestia Control Panel can be found in the [Development](docs/docs/contributing/development.md) documentation.
### Code formatting and comments
We ask that you follow existing naming schemes and coding conventions where possible, and that you add comments in your source code where appropriate to aid other developers in debugging and understanding your code in the future.
To ensure your changes meet our formatting requirements, please run `npm install` from the root of the repository before committing your changes. This will set up pre-commit hooks for automatic formatting, which will help to get your changes merged as quickly as possible.
### Workflow and process
Development for this project takes place in branches to effectively develop, manage, and test new features and code changes. Our tiered approach allows us to closely control the quality of code as it is checked in for inclusion.
We have three primary or "evergreen" branches, which exist throughout our product's lifetime. Please refer to the following table for a description:
| Branch | Description |
| --------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| `main` | Contains a snapshot of the latest development code.<br>**Not intended for production use and contains code from a merge snapshot.** |
| `beta` | Contains a snapshot of the next version which is currently in testing.<br>**Not intended for production use, however code from this branch should be stable.** |
| `release` | Contains a snapshot of the latest stable release.<br>**Intended for production use. This repository contains the same code as our compiled packages.** |
### Creating a new branch and submitting pull requests
The first step is to create a fork of the `hestiacp/hestiacp` repository under your GitHub account so that you may submit pull requests and patches.
Once you've created your fork, clone the repository to your computer and make sure that you've checked out the `main` branch. **Always** create a new topic branch for your work.
### Branch naming convention
- **Prefix:** `topic/` (such as **fix**, **feature**, **refactor**, etc.)
- **ID**: `888` (GitHub Issue ID if an issue exists)
- **Title:** `my-awesome-patch`
Branch name examples:
- `feature/777-my-awesome-new-feature` or `feature/my-other-new-feature`
- `fix/000-some-bug-fix` or `fix/this-feature-is-broken`
- `refactor/v-change-domain-owner`
- `test/mail-domain-ssl`
### Squashing commits for smaller changes
To aid other developers and keep the project's commit history clean, please **squash your commits** when it's appropriate. For example with smaller commits related to the same piece of code, such as commits labelled "Fixed item 1", "Adjusted color of button XYZ", "Adjusted alignment of button XYZ" can be squashed into one commit with the title "Fixed button issues in item".
### What happens when I submit a pull request?
- Our internal development team will review your work and validate your request.
- Your changes will be tested to ensure that there are no issues.
- If changes need to be made, you will be notified via GitHub.
- Once approved, your code will be merged for inclusion in an upcoming release of Hestia Control Panel.
All pull requests must include a brief but descriptive title, and a detailed description of the changes that you've made. **Only include commits that are related to your feature, bug fix, or patch in your pull request!**
## Thank you
We appreciate **all** contributions no matter what size; your feedback and input directly shapes the future of Hestia Control Panel and we could not do it without your support.
Thank you for your time and we look forward to seeing your pull requests,<br>
The Hestia Control Panel development team

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. 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
them 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 prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. 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.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey 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;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If 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 convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU 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 that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
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.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
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.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
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
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program 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, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU 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. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

@ -0,0 +1,138 @@
<h1 align="center"><a href="https://www.hestiacp.com/">Hestia Control Panel</a></h1>
![HestiaCP Web Interface screenshot](https://storage.hestiacp.com/hestiascreen.png)
<h2 align="center">Lightweight and powerful control panel for the modern web</h2>
<p align="center"><strong>Latest stable release:</strong> Version 1.8.2 | <a href="https://github.com/hestiacp/hestiacp/blob/release/CHANGELOG.md">View Changelog</a></p>
<p align="center">
<a href="https://www.hestiacp.com/">HestiaCP.com</a> |
<a href="https://docs.hestiacp.com/">Documentation</a> |
<a href="https://forum.hestiacp.com/">Forum</a>
<br/><br/>
<a href="https://drone.hestiacp.com/hestiacp/hestiacp">
<img src="https://drone.hestiacp.com/api/badges/hestiacp/hestiacp/status.svg?ref=refs/heads/main" alt="Drone Status"/>
</a>
<a href="https://github.com/hestiacp/hestiacp/actions/workflows/lint.yml">
<img src="https://github.com/hestiacp/hestiacp/actions/workflows/lint.yml/badge.svg" alt="Lint Status"/>
</a>
</p>
## **Welcome!**
Hestia Control Panel is designed to provide administrators an easy to use web and command line interface, enabling them to quickly deploy and manage web domains, mail accounts, DNS zones, and databases from one central dashboard without the hassle of manually deploying and configuring individual components or services.
## Donate
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ST87LQH2CHGLA)<br /><br />
Bitcoin : bc1q48jt5wg5jaj8g9zy7c3j03cv57j2m2u5anlutu<br>
Ethereum : 0xfF3Dd2c889bd0Ff73d8085B84A314FC7c88e5D51<br>
Binance: bnb1l4ywvw5ejfmsgjdcx8jn5lxj7zsun8ktfu7rh8<br>
Smart Chain: 0xfF3Dd2c889bd0Ff73d8085B84A314FC7c88e5D51<br>
## Features and Services
- Apache2 and NGINX with PHP-FPM
- Multiple PHP versions (5.6 - 8.2, 8.0 as default)
- DNS Server (Bind) with clustering capabilities
- POP/IMAP/SMTP mail services with Anti-Virus, Anti-Spam, and Webmail (ClamAV, SpamAssassin, Sieve, Roundcube)
- MariaDB/MySQL and/or PostgreSQL databases
- Let's Encrypt SSL support with wildcard certificates
- Firewall with brute-force attack detection and IP lists (iptables, fail2ban, and ipset).
## Supported platforms and operating systems
- **Debian:** 12, 11, or 10
- **Ubuntu:** 22.04 LTS, 20.04 LTS
**NOTES:**
- Hestia Control Panel does not support 32 bit operating systems!
- Hestia Control Panel in combination with OpenVZ 7 or lower might have issues with DNS and/or firewall. If you use a Virtual Private Server we strongly advice you to use something based on KVM or LXC!
## Installing Hestia Control Panel
- **NOTE:** You must install Hestia Control Panel on top of a fresh operating system installation to ensure proper functionality.
While we have taken every effort to make the installation process and the control panel interface as friendly as possible (even for new users), it is assumed that you will have some prior knowledge and understanding in the basics how to set up a Linux server before continuing.
### Step 1: Log in
To start the installation, you will need to be logged in as **root** or a user with super-user privileges. You can perform the installation either directly from the command line console or remotely via SSH:
```bash
ssh root@your.server
```
### Step 2: Download
Download the installation script for the latest release:
```bash
wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh
```
If the download fails due to an SSL validation error, please be sure you've installed the ca-certificate package on your system - you can do this with the following command:
```bash
apt-get update && apt-get install ca-certificates
```
### Step 3: Run
To begin the installation process, simply run the script and follow the on-screen prompts:
```bash
bash hst-install.sh
```
You will receive a welcome email at the address specified during installation (if applicable) and on-screen instructions after the installation is completed to log in and access your server.
### Custom installation
You may specify a number of various flags during installation to only install the features in which you need. To view a list of available options, run:
```bash
bash hst-install.sh -h
```
Alternatively, You can use <https://hestiacp.com/install.html> which allows you to easily generate the installation command via GUI.
## How to upgrade an existing installation
Automatic Updates are enabled by default on new installations of Hestia Control Panel and can be managed from **Server Settings > Updates**. To manually check for and install available updates, use the apt package manager:
```bash
apt-get update
apt-get upgrade
```
## Issues & Support Requests
- If you encounter a general problem while using Hestia Control Panel and need help, please [visit our forum](https://forum.hestiacp.com/) to search for potential solutions or post a new thread where community members can assist.
- Bugs and other reproducible issues should be filed via GitHub by [creating a new issue report](https://github.com/hestiacp/hestiacp/issues) so that our developers can investigate further. Please note that requests for support will be redirected to our forum.
**IMPORTANT: We _cannot_ provide support for requests that do not describe the troubleshooting steps that have already been performed, or for third-party applications not related to Hestia Control Panel (such as WordPress). Please make sure that you include as much information as possible in your forum posts or issue reports!**
## Contributions
If you would like to contribute to the project, please [read our Contribution Guidelines](https://github.com/hestiacp/hestiacp/blob/release/CONTRIBUTING.md) for a brief overview of our development process and standards.
## Copyright
"Hestia Control Panel", "HestiaCP", and the Hestia logo are original copyright of hestiacp.com and the following restrictions apply:
**You are allowed to:**
- use the names "Hestia Control Panel", "HestiaCP", or the Hestia logo in any context directly related to the application or the project. This includes the application itself, local communities and news or blog posts.
**You are not allowed to:**
- sell or redistribute the application under the name "Hestia Control Panel", "HestiaCP", or similar derivatives, including the use of the Hestia logo in any brand or marketing materials related to revenue generating activities,
- use the names "Hestia Control Panel", "HestiaCP", or the Hestia logo in any context that is not related to the project,
- alter the name "Hestia Control Panel", "HestiaCP", or the Hestia logo in any way.
## License
Hestia Control Panel is licensed under [GPL v3](https://github.com/hestiacp/hestiacp/blob/release/LICENSE) license, and is based on the [VestaCP](https://vestacp.com/) project.<br>

@ -0,0 +1,51 @@
# Hestia CP Security policy
Welcome and thanks for taking interest in Hestia CP!
We are mostly interested in reports by actual Hestia CP users but all high quality contributions are welcome.
If you believe that you have have discovered a vulnerability in Hestia Control Panel,
please let our development team know by submitting a report [Huntr.dev](https://huntr.dev/bounties/disclose/?target=https://github.com/hestiacp/hestiacp) Bounties and CVEs are automatically managed and allocated via the platform.
If you are unable to use [Huntr.dev](https://huntr.dev/bounties/disclose/?target=https://github.com/hestiacp/hestiacp) please send an email to <info@hestiacp.com>
We ask you to include a detailed description of the vulnerability, a list of services involved (e.g. exim, dovecot) and the versions which you've tested, full steps to reproduce the vulnerability, and include your findings and expected results.
Please do not open any public issue on Github or any other social media before the report has been published and a fix has been released.
With that, good luck hacking us ;)
## Supported versions
| Version | Supported |
| ------- | ------------------ |
| Latest | :white_check_mark: |
## Qualifying Vulnerabilities
### Vulnerabilities we really care about
- Remote command execution
- Code/SQL Injection
- Authentication bypass
- Privilege Escalation
- Cross-site scripting (XSS)
- Performing limited admin actions without authorization
- CSRF
### Vulnerabilities we accept
- Open redirects
- Password brute-forcing that circumvents rate limiting
## Non-Qualifying Vulnerabilities
- Theoretical attacks without proof of exploitability
- Attacks that are the result of a third party library should be reported to the library maintainers
- Social engineering
- Reflected file download
- Physical attacks
- Weak SSL/TLS/SSH algorithms or protocols
- Attacks involving physical access to a users device, or involving a device or network thats already seriously compromised (eg man-in-the-middle).
- The user attacks themselves
- anything in `/test/` folder

@ -0,0 +1,81 @@
#!/bin/bash
# info: update user notification
# options: USER NOTIFICATION
#
# example: v-acknowledge-user-notification
#
# This function updates user notification.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
id=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER NOTIFICATION'
if [ "$id" = "all" ]; then
is_format_valid 'user'
else
is_format_valid 'user' 'id'
fi
is_object_valid 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Updating notification
if [ "$id" = 'all' ]; then
for id in $(v-list-user-notifications admin plain | cut -f1); do
update_object_value 'notifications' 'NID' "$id" '$ACK' 'yes' 2> /dev/null
done
else
update_object_value 'notifications' 'NID' "$id" '$ACK' 'yes' 2> /dev/null
# Checking last notification
if [ -e "$USER_DATA/notifications.conf" ]; then
if [ -z "$(grep NID= $USER_DATA/notifications.conf)" ]; then
notice='no'
fi
if [ -z "$(grep "ACK='no'" $USER_DATA/notifications.conf)" ]; then
notice='no'
fi
else
notice='no'
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Updating notification counter
if [ "$notice" = 'no' ]; then
if [ -z "$(grep NOTIFICATIONS $USER_DATA/user.conf)" ]; then
sed -i "s/^TIME/NOTIFICATIONS='no'\nTIME/g" $USER_DATA/user.conf
else
update_user_value "$user" '$NOTIFICATIONS' "no"
fi
fi
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,106 @@
#!/bin/bash
# info: generate access key
# options: USER [PERMISSIONS] [COMMENT] [FORMAT]
#
# example: v-add-access-key admin v-purge-nginx-cache,v-list-mail-accounts comment json
#
# The "PERMISSIONS" argument is optional for the admin user only.
# This function creates a key file in $HESTIA/data/access-keys/
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
permissions=$2
comment=$3
format=${4-shell}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
keygen() {
local LENGTH=${1:-20}
local USE_SPECIAL_CHARACTERS="${2:-no}"
local MATRIX='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
if [[ "$USE_SPECIAL_CHARACTERS" == "yes" ]]; then
MATRIX+='_-='
fi
local PASS N
while [ ${N:=1} -le $LENGTH ]; do
PASS="$PASS${MATRIX:$(($RANDOM % ${#MATRIX})):1}"
let N+=1
done
echo "$PASS"
}
access_key_id="$(keygen)"
secret_access_key="$(keygen 40 yes)"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
# Remove whitespace and bin path from permissions
permissions="$(cleanup_key_permissions "$permissions")"
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'USER [PERMISSIONS] [COMMENT] [FORMAT]'
is_format_valid 'user' 'comment' 'format'
is_object_valid 'user' 'USER' "$user"
is_key_permissions_format_valid "$permissions" "$user"
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
if [ ! -d "$HESTIA/data/access-keys/" ]; then
mkdir -p $HESTIA/data/access-keys/
chown root:root $HESTIA/data/access-keys/
chmod 750 $HESTIA/data/access-keys/
fi
if [[ -e "$HESTIA/data/access-keys/${access_key_id}" ]]; then
while [[ -e "$HESTIA/data/access-keys/${access_key_id}" ]]; do
access_key_id=$(keygen)
done
fi
echo "SECRET_ACCESS_KEY='$secret_access_key'" > "$HESTIA/data/access-keys/${access_key_id}"
echo "USER='$user'" >> "$HESTIA/data/access-keys/${access_key_id}"
echo "PERMISSIONS='$permissions'" >> "$HESTIA/data/access-keys/${access_key_id}"
echo "COMMENT='$comment'" >> "$HESTIA/data/access-keys/${access_key_id}"
echo "TIME='$time'" >> "$HESTIA/data/access-keys/${access_key_id}"
echo "DATE='$date'" >> "$HESTIA/data/access-keys/${access_key_id}"
# TODO Index reserved for future implementation
echo "EXPIRES_IN=''" >> "$HESTIA/data/access-keys/${access_key_id}"
echo "IP=''" >> "$HESTIA/data/access-keys/${access_key_id}"
chmod 640 "$HESTIA/data/access-keys/${access_key_id}"
$BIN/v-list-access-key "$access_key_id" "$format"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
log_history "Access key $access_key_id generated" "Warning" "$user" "API"
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,344 @@
#!/bin/bash
# info: add backup host
# options: TYPE HOST USERNAME PASSWORD [PATH] [PORT]
#
# example: v-add-backup-host sftp backup.acme.com admin p4$$w@Rd
# v-add-backup-host b2 bucketName keyID applicationKey
#
# Add a new remote backup location. Currently SFTP, FTP and Backblaze are supported
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
type=$1
host=$2
user=$3
raw_password=$4
HIDE=4
password=$(perl -e 'print quotemeta shift(@ARGV)' "${raw_password}")
path=${5-/backup}
port=$6
# CPU Architecture
arch=$(arch)
# Includes
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Fetch current verison B2 CLI tool
source_conf "$HESTIA/install/upgrade/upgrade.conf"
# Paths
b2cli="/usr/local/bin/b2"
b2lnk="https://github.com/Backblaze/B2_Command_Line_Tool/releases/download/v$b2_v/b2-linux"
# Defining ftp command function
ftpc() {
ftp -p -n $host $port << EOF
quote USER $user
quote PASS $password
binary
$1
$2
$3
quit
EOF
}
# Defining sftp command function
sftpc() {
if [ "$privatekey" != "yes" ]; then
expect -f "-" "$@" << EOF
set count 0
spawn /usr/bin/sftp -o StrictHostKeyChecking=no -o Port=$port $user@$host
expect {
-nocase "password:" {
send "$password\r"
exp_continue
}
-re "Password for (.*)@(.*)" {
send "$password\r"
exp_continue
}
-re "Couldn't|(.*)disconnect|(.*)stalled|(.*)not found" {
set count \$argc
set output "Disconnected."
set rc $E_FTP
exp_continue
}
-re ".*denied.*(publickey|password)." {
set output "Permission denied, wrong publickey or password."
set rc $E_CONNECT
}
"sftp>" {
if {\$count < \$argc} {
set arg [lindex \$argv \$count]
send "\$arg\r"
incr count
} else {
send "exit\r"
set output "Disconnected."
if {[info exists rc] != 1} {
set rc $OK
}
}
exp_continue
}
timeout {
set output "Connection timeout."
set rc $E_CONNECT
}
}
if {[info exists output] == 1} {
puts "\$output"
}
exit \$rc
EOF
else
expect -f "-" "$@" << EOF
set count 0
spawn /usr/bin/sftp -o StrictHostKeyChecking=no -o Port=$port -i $raw_password $user@$host
expect {
-re "Couldn't|(.*)disconnect|(.*)stalled|(.*)not found" {
set count \$argc
set output "Disconnected."
set rc $E_FTP
exp_continue
}
-re ".*denied.*(publickey|password)." {
set output "Permission denied, wrong publickey or password."
set rc $E_CONNECT
}
"sftp>" {
if {\$count < \$argc} {
set arg [lindex \$argv \$count]
send "\$arg\r"
incr count
} else {
send "exit\r"
set output "Disconnected."
if {[info exists rc] != 1} {
set rc $OK
}
}
exp_continue
}
timeout {
set output "Connection timeout."
set rc $E_CONNECT
}
}
if {[info exists output] == 1} {
puts "\$output"
}
exit \$rc
EOF
fi
}
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
if [ "$type" != 'local' ]; then
check_args '2' "$#" "TYPE HOST USERNAME PASSWORD [PATH] [PORT]"
is_format_valid 'host' 'path' 'port'
is_type_valid 'sftp,ftp,b2,rclone' "$type"
is_username_format_valid "$user" "username"
privatekey="no"
if [ -f "$raw_password" ]; then
if [[ $(cat "$raw_password" | grep "OPENSSH PRIVATE") ]]; then
privatekey="yes"
password="$raw_password"
else
is_password_valid
fi
else
is_password_valid
fi
format_no_quotes "$password" "password"
if [ "$type" = 'sftp' ]; then
which expect > /dev/null 2>&1
check_result $? "expect command not found" "$E_NOTEXIST"
fi
if [ "$type" != 'b2' ] && [ "$type" != 'rclone' ]; then
if ! (is_ip_format_valid "$host" > /dev/null); then
host "$host" > /dev/null 2>&1
check_result $? "host connection failed" "$E_CONNECT"
fi
fi
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Checking network connection
if [ "$type" = 'ftp' ]; then
if [ -z $port ]; then
port=21
fi
fconn=$(ftpc 2>&1)
ferror=$(echo $fconn \
| grep -i -e failed -e error -e "can't" -e "not conn" -e "incorrect")
if [ -n "$ferror" ]; then
echo "Error: can't login to ftp $user@$host"
log_event "$E_CONNECT" "$ARGUMENTS"
exit "$E_CONNECT"
fi
# Checking write permissions
if [ -z $path ]; then
ftmpdir="vst.bK76A9SUkt"
else
ftpc "mkdir $path" > /dev/null 2>&1
ftmpdir="$path/vst.bK76A9SUkt"
fi
ftp_result=$(ftpc "mkdir $ftmpdir" "rm $ftmpdir" | grep -v Trying)
if [ -n "$ftp_result" ]; then
echo "$ftp_result"
rm -rf $tmpdir
echo "Error: can't create $ftmpdir folder on the ftp"
log_event "$E_FTP" "$ARGUMENTS"
exit "$E_FTP"
fi
fi
if [ "$type" = 'sftp' ]; then
if [ -z $port ]; then
port=22
fi
if [ -z $path ]; then
sftmpdir="vst.bK76A9SUkt"
sftpc "mkdir $sftmpdir" "rmdir $sftmpdir" > /dev/null 2>&1
else
if sftpc "mkdir $path" > /dev/null 2>&1; then
sftmpdir="$path/vst.bK76A9SUkt"
sftpc "mkdir $sftmpdir" "rmdir $sftmpdir" > /dev/null 2>&1
else
sftmpdir="$path/vst.bK76A9SUkt"
sftpc "mkdir $sftmpdir" "rmdir $sftmpdir" > /dev/null 2>&1
fi
fi
rc=$?
if [[ "$rc" != 0 ]]; then
case $rc in
$E_CONNECT) echo "Error: can't login to sftp $user@$host" ;;
$E_FTP) echo "Error: can't create temp folder on the sftp host" ;;
esac
log_event "$rc" "$ARGUMENTS"
exit "$rc"
fi
fi
if [ "$type" = 'b2' ]; then
# Download b2 binary
if [ ! -f "$b2cli" ]; then
if [ "$arch" = 'aarch64' ] || [ "$arch" = 'arm64' ]; then
echo "Error: B2 binary for arm64 must be downloaded manually."
exit 3
else
wget -O $b2cli $b2lnk > /dev/null 2>&1
chmod +x $b2cli > /dev/null 2>&1
fi
if [ ! -f "$b2cli" ]; then
echo "Error: Binary download failed, b2 doesn't work as expected."
exit 3
fi
fi
# Validate b2 binary
b2version="$(b2 version)"
if [[ ! "$b2version" =~ "b2 command line tool" ]]; then
echo "Error: Binary download failed, b2 doesn't work as expected."
exit 3
fi
b2 clear-account > /dev/null 2>&1
b2 authorize-account "$user" "$raw_password" > /dev/null 2>&1
b2 ls --long "$host" "$user" > /dev/null 2>&1
if [ $? -ne 0 ]; then
check_result "$E_CONNECT" "b2 failed to verify connection"
fi
fi
if [ "$type" = 'rclone' ]; then
curl -s https://rclone.org/install.sh | bash /dev/null > /dev/null 2>&1
# Verify account exists
if [ ! -z "$(cat /root/.config/rclone/rclone.conf | grep "\[$host\]")" ]; then
echo "test" > /tmp/hestia-backup.txt
# Try to upload a single file
if [ -z "$path" ]; then
rclone copy /tmp/hestia-backup.txt $host:/hestia-backup.txt
rclone delete $host:/hestia-backup.txt
else
rclone copy /tmp/hestia-backup.txt $host:$path/hestia-backup.txt
rclone delete $host:$path/hestia-backup.txt
fi
else
check_result "$E_CONNECT" "Rclone config does not exist"
fi
fi
# Adding backup host
if [ $type == 'ftp' ] || [ $type = 'sftp' ]; then
new_timestamp
str="HOST='$host'\nUSERNAME='$user'\nPASSWORD='$password'\nPRIVATEKEY='$privatekey'"
str="$str\nBPATH='$path'\nPORT='$port'\nTIME='$time'\nDATE='$date'"
echo -e "$str" > $HESTIA/conf/$type.backup.conf
chmod 660 $HESTIA/conf/$type.backup.conf
elif [ $type == 'b2' ]; then
new_timestamp
str="BUCKET='$host'\nB2_KEYID='$user'\nB2_KEY='$raw_password'"
str="$str\nTIME='$time'\nDATE='$date'"
echo -e "$str" > $HESTIA/conf/$type.backup.conf
chmod 660 $HESTIA/conf/$type.backup.conf
elif [ $type == "rclone" ]; then
new_timestamp
str="HOST='$host'\nBPATH='$path'"
str="$str\nTIME='$time'\nDATE='$date'"
echo -e "$str" > $HESTIA/conf/$type.backup.conf
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Update hestia.conf
if [ -z "$(grep BACKUP_SYSTEM $HESTIA/conf/hestia.conf)" ]; then
echo "BACKUP_SYSTEM='$type'" >> $HESTIA/conf/hestia.conf
else
bckp=$(echo "$BACKUP_SYSTEM,$type" \
| sed "s/,/\n/g" \
| sort -r -u \
| sed "/^$/d" \
| sed ':a;N;$!ba;s/\n/,/g')
sed -i "s/BACKUP_SYSTEM=.*/BACKUP_SYSTEM='$bckp'/g" $HESTIA/conf/hestia.conf
fi
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,99 @@
#!/bin/bash
# info: add cron job for hestia automatic updates
# options: MODE
#
# This function adds a cronjob for hestia automatic updates
# that can be downloaded from apt or git.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=admin
mode=$1
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
is_system_enabled "$CRON_SYSTEM" 'CRON_SYSTEM'
is_package_full 'CRON_JOBS'
get_next_cronjob
check_cron_apt=$(grep 'v-update-sys-hestia-all' $USER_DATA/cron.conf)
check_cron_git=$(grep 'v-update-sys-hestia-git' $USER_DATA/cron.conf)
if [ -n "$check_cron_apt" ] || [ -n "$check_cron_git" ]; then
exit
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Define time somewhere at night
if [ -z "$mode" ] || [ "$mode" = "apt" ]; then
min=$(generate_password '012345' '2')
hour=$(generate_password '1234567' '1')
day='*'
month='*'
wday='*'
command="sudo $BIN/v-update-sys-hestia-all"
fi
if [ "$mode" = "git" ]; then
min='0'
hour='0'
day='*'
month='*'
wday='*'
command="sudo $BIN/v-update-sys-hestia-git"
fi
# Concatenating cron string
str="JOB='$job' MIN='$min' HOUR='$hour' DAY='$day' MONTH='$month' WDAY='$wday'"
str="$str CMD='$command' SUSPENDED='no' TIME='$time' DATE='$date'"
# Adding to crontab
echo "$str" >> $HESTIA/data/users/$user/cron.conf
# Changing permissions
chmod 660 $HESTIA/data/users/$user/cron.conf
# Sort jobs by id number
sort_cron_jobs
# Sync cronjobs with system cron
sync_cron_jobs
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Increasing cron value
increase_user_value "$user" '$U_CRON_JOBS'
# Restarting cron
$BIN/v-restart-cron
check_result $? "Cron restart failed" > /dev/null
# Logging
$BIN/v-log-action "system" "Info" "Updates" "Automatic updates enabled."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,93 @@
#!/bin/bash
# info: add cron job
# options: USER MIN HOUR DAY MONTH WDAY COMMAND [JOB] [RESTART]
#
# example: v-add-cron-job admin * * * * * sudo /usr/local/hestia/bin/v-backup-users
#
# This function adds a job to cron daemon. When executing commands, any output
# is mailed to user's email if parameter REPORTS is set to 'yes'.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
min=$2
hour=$3
day=$4
month=$5
wday=$6
command=$(echo $7 | sed "s/'/%quote%/g")
job=$8
restart=$9
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
HIDE=7
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '7' "$#" 'USER MIN HOUR DAY MONTH WDAY COMMAND [JOB] [RESTART]'
is_format_valid 'user' 'min' 'hour' 'day' 'month' 'wday' 'command'
is_system_enabled "$CRON_SYSTEM" 'CRON_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_package_full 'CRON_JOBS'
get_next_cronjob
is_format_valid 'job'
is_format_valid 'restart'
is_object_new 'cron' 'JOB' "$job"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Concatenating cron string
str="JOB='$job' MIN='$min' HOUR='$hour' DAY='$day' MONTH='$month' WDAY='$wday'"
str="$str CMD='$command' SUSPENDED='no' TIME='$time' DATE='$date'"
# Adding to crontab
echo "$str" >> $HESTIA/data/users/$user/cron.conf
# Changing permissions
chmod 660 $HESTIA/data/users/$user/cron.conf
# Sort jobs by id number
sort_cron_jobs
# Sync cronjobs with system crond
sync_cron_jobs
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Increasing cron value
increase_user_value $user '$U_CRON_JOBS'
# Restarting cron
$BIN/v-restart-cron "$restart"
check_result $? "Cron restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "Cron Jobs" "Cron job added (ID: $job, Command: $command)"
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,46 @@
#!/bin/bash
# info: add cron job for Let's Encrypt certificates
# options: NONE
#
# This function adds a new cron job for Let's Encrypt.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
is_system_enabled "$CRON_SYSTEM" 'CRON_SYSTEM'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Add cron job
cmd="sudo $BIN/v-update-sys-queue letsencrypt"
check_cron=$(grep "$cmd" $HESTIA/data/users/admin/cron.conf 2> /dev/null)
if [ -z "$check_cron" ] && [ -n "$CRON_SYSTEM" ]; then
$BIN/v-add-cron-job admin '*/5' '*' '*' '*' '*' "$cmd"
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,60 @@
#!/bin/bash
# info: add cron reports
# options: USER
#
# example: v-add-cron-reports admin
#
# This function for enabling reports on cron tasks and administrative
# notifications.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'USER'
is_format_valid 'user'
is_system_enabled "$CRON_SYSTEM" 'CRON_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Changing user report value
update_user_value "$user" '$CRON_REPORTS' 'yes'
# Sync system cron with user
sync_cron_jobs
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restart cron
$BIN/v-restart-cron
check_result $? "Cron restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "Cron Jobs" "Cron job notifications and reporting enabled."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,46 @@
#!/bin/bash
# info: add cron reports
# options: NONE
#
# This function for enabling restart cron tasks
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
is_system_enabled "$CRON_SYSTEM" 'CRON_SYSTEM'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Add cron job
cmd="sudo $BIN/v-update-sys-queue restart"
check_cron=$(grep "$cmd" $HESTIA/data/users/admin/cron.conf 2> /dev/null)
if [ -z "$check_cron" ] && [ -n "$CRON_SYSTEM" ]; then
$BIN/v-add-cron-job admin '*' '*' '*' '*' '*' "$cmd"
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,113 @@
#!/bin/bash
# info: add database
# options: USER DATABASE DBUSER DBPASS [TYPE] [HOST] [CHARSET]
#
# example: v-add-database admin wordpress_db matt qwerty123
#
# This function creates the database concatenating username and user_db.
# Supported types of databases you can get using v-list-sys-config script.
# If the host isn't stated and there are few hosts configured on the server,
# then the host will be defined by one of three algorithms. "First" will choose
# the first host in the list. "Random" will chose the host by a chance.
# "Weight" will distribute new database through hosts evenly. Algorithm and
# types of supported databases is designated in the main configuration file.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
database="$user"_"$2"
dbuser="$user"_"$3"
password=$4
HIDE=4
type=${5-mysql}
host=$6
charset=${7-UTF8MB4}
charset=$(echo "$charset" | tr '[:lower:]' '[:upper:]')
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/db.sh
source $HESTIA/func/db.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
if [ "$type" = "pgsql" ]; then
database=$(echo "$user"_"$2" | tr '[:upper:]' '[:lower:]')
dbuser=$(echo "$user"_"$3" | tr '[:upper:]' '[:lower:]')
fi
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '4' "$#" 'USER DATABASE DBUSER DBPASS [TYPE] [HOST] [CHARSET]'
is_format_valid 'user' 'database' 'dbuser' 'charset'
is_system_enabled "$DB_SYSTEM" 'DB_SYSTEM'
is_type_valid "$DB_SYSTEM" "$type"
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_new 'db' 'DB' "$database"
is_object_new 'db' 'DBUSER' "$dbuser"
get_next_dbhost
is_object_valid "../../../conf/$type" 'HOST' "$host"
is_object_unsuspended "../../../conf/$type" 'DBHOST' "$host"
#is_charset_valid
is_package_full 'DATABASES'
is_password_valid
if [ "$type" = "pgsql" ]; then
exclude="-"
if [[ "$dbuser" =~ $exclude ]]; then
check_result "$E_INVALID" "invalid database user format"
fi
if [[ "$database" =~ $exclude ]]; then
check_result "$E_INVALID" "invalid database format"
fi
fi
dbpass="$password"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Switching on db type
case $type in
mysql) add_mysql_database ;;
pgsql) add_pgsql_database ;;
esac
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Adding db to db conf
str="DB='$database' DBUSER='$dbuser' MD5='$md5' HOST='$host' TYPE='$type'"
str="$str CHARSET='$charset' U_DISK='0' SUSPENDED='no' TIME='$time'"
str="$str DATE='$date'"
echo "$str" >> $USER_DATA/db.conf
chmod 660 $USER_DATA/db.conf
# Increasing counters
increase_dbhost_values
increase_user_value "$user" '$U_DATABASES'
# Logging
$BIN/v-log-action "$user" "Info" "Databases" "Added new database $database ($type)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,169 @@
#!/bin/bash
# info: add new database server
# options: TYPE HOST DBUSER DBPASS [MAX_DB] [CHARSETS] [TEMPLATE] [PORT]
#
# example: v-add-database-host mysql localhost alice p@$$wOrd
#
# This function add new database server to the server pool. It supports local
# and remote database servers, which is useful for clusters. By adding a host
# you can set limit for number of databases on a host. Template parameter is
# used only for PostgreSQL and has an default value "template1". You can read
# more about templates in official PostgreSQL documentation.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
type=$1
host=$2
dbuser=$3
password=$4
HIDE=4
max_db=${5-500}
charsets=${6}
template=${7}
port=${8}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/db.sh
source $HESTIA/func/db.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
is_mysql_host_alive() {
mycnf=$(mktemp)
echo "[client]" > $mycnf
echo "host='$host'" >> $mycnf
echo "user='$dbuser'" >> $mycnf
echo "password='$password'" >> $mycnf
echo "port='$port'" >> $mycnf
chmod 600 $mycnf
mysql_out=$(mktemp)
if [ -f '/usr/bin/mariadb' ]; then
mariadb --defaults-file=$mycnf -e 'SELECT VERSION()' > $mysql_out 2>&1
else
mysql --defaults-file=$mycnf -e 'SELECT VERSION()' > $mysql_out 2>&1
fi
if [ "$?" -ne '0' ]; then
echo "Error: MySQL connection to $host failed"
rm $mycnf
log_event "$E_CONNECT" "$ARGUMENTS"
exit "$E_CONNECT"
fi
if [ -f '/usr/bin/mariadb' ]; then
grants=$(mariadb --defaults-file=$mycnf -e 'SHOW GRANTS FOR CURRENT_USER();')
else
grants=$(mysql --defaults-file=$mycnf -e 'SHOW GRANTS FOR CURRENT_USER();')
fi
if [ "$?" -ne '0' ]; then
echo "Error: MySQL connection to $host failed"
rm $mycnf
log_event "$E_CONNECT" "$ARGUMENTS"
exit "$E_CONNECT"
fi
# Check allow to grant user
check_grants=$(echo $grants | grep "WITH GRANT OPTION")
if [ -z "$check_grants" ]; then
echo "Error: MySQL connection to $host failed. Unable to grant other users"
rm $mycnf
log_event "$E_CONNECT" "$ARGUMENTS"
exit "$E_CONNECT"
fi
rm $mycnf
}
is_pgsql_host_alive() {
export PGPASSWORD="$dbpass"
psql -h $host -U $dbuser -p $port -c "SELECT VERSION()" > /dev/null 2>&1
if [ "$?" -ne '0' ]; then
echo "Error: PostgreSQL connection to $host failed"
log_event "$E_CONNECT" "$ARGUMENTS"
exit "$E_CONNECT"
fi
}
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
args_usage='TYPE HOST DBUSER DBPASS [MAX_DB] [CHARSETS] [TPL] [PORT]'
check_args '4' "$#" "$args_usage"
if [ -z $charsets ]; then charsets="UTF8,LATIN1,WIN1250,WIN1251,WIN1252,WIN1256,WIN1258,KOI8"; fi
if [ -z $template ]; then template="template1"; fi
database_set_default_ports
is_format_valid 'host' 'dbuser' 'max_db' 'charsets' 'template' 'port'
is_system_enabled "$DB_SYSTEM" 'DB_SYSTEM'
is_type_valid "$DB_SYSTEM" "$type"
is_dbhost_new
is_password_valid
dbpass="$password"
case $type in
mysql) is_mysql_host_alive ;;
pgsql) is_pgsql_host_alive ;;
esac
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Concatenating db host string
case $type in
mysql)
str="HOST='$host' USER='$dbuser' PASSWORD='$dbpass'"
str="$str CHARSETS='$charsets' MAX_DB='$max_db' U_SYS_USERS=''"
str="$str U_DB_BASES='0' SUSPENDED='no' TIME='$time' DATE='$date' PORT='$port'"
;;
pgsql)
str="HOST='$host' USER='$dbuser' PASSWORD='$dbpass'"
str="$str CHARSETS='$charsets' TPL='$template' MAX_DB='$max_db'"
str="$str U_SYS_USERS='' U_DB_BASES='0' SUSPENDED='no'"
str="$str TIME='$time' DATE='$date' PORT='$port'"
;;
esac
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding host to conf
echo "$str" >> $HESTIA/conf/$type.conf
chmod 660 $HESTIA/conf/$type.conf
# Updating hestia.conf
if [ -z "$(grep DB_SYSTEM $HESTIA/conf/hestia.conf)" ]; then
echo "DB_SYSTEM='$type'" >> $HESTIA/conf/hestia.conf
else
db=$(echo "$DB_SYSTEM,$type" \
| sed "s/,/\n/g" \
| sort -r -u \
| sed "/^$/d" \
| sed ':a;N;$!ba;s/\n/,/g')
sed -i "s/DB_SYSTEM=.*/DB_SYSTEM='$db'/g" $HESTIA/conf/hestia.conf
fi
# Logging
$BIN/v-log-action "system" "Info" "Database" "Added external $type database server ($host) to the system."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,87 @@
#!/bin/bash
# info: add temp database user
# options: USER DATABASE [TYPE] [HOST] [TTL]
#
# example: v-add-database-temp-user wordress wordpress_db mysql
#
# This function creates an temporary database user mysql_sso_db_XXXXXXXX and a random password
# The user has an limited validity and only granted access to the specific database
# Returns json to be read SSO Script
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
database="$2"
type=${3-mysql}
host=$4
ttl=$5
if [ "$ttl" == '' ]; then
ttl=60
fi
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/db.sh
source $HESTIA/func/db.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DATABASE [TYPE] [HOST] [TTL]'
is_format_valid 'user' 'database' 'ttl'
is_type_valid "$DB_SYSTEM" "$type"
is_system_enabled "$DB_SYSTEM" 'DB_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'db' 'DB' "$database"
is_object_unsuspended 'db' 'DB' "$database"
get_next_dbhost
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Get database values
get_database_values
#generate password and unique user
dbpass=$(generate_password)
dbuser="hestia_sso_$(generate_password)"
add_mysql_database_temp_user
if [ $? -ne 0 ]; then
echo "Error: Unable to create temp user"
exit 2
fi
if [[ "$ttl" -gt 0 ]]; then
echo "$BIN/v-delete-database-temp-user $user $database $dbuser mysql $host" | at "now +${ttl} minute" > /dev/null 2>&1
fi
echo '{
"login": {
"user": "'$dbuser'",
"password": "'$dbpass'"
}
}'
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "$user" "Info" "Databases" "Granted user $dbuser access to database $database."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,222 @@
#!/bin/bash
# info: add dns domain
# options: USER DOMAIN IP [NS1] [NS2] [NS3] [NS4] [NS5] [NS6] [NS7] [NS8] [RESTART]
#
# example: v-add-dns-domain admin example.com ns1.example.com ns2.example.com '' '' '' '' '' '' yes
#
# This function adds DNS zone with records defined in the template. If the exp
# argument isn't stated, the expiration date value will be set to next year.
# The soa argument is responsible for the relevant record. By default the first
# user's NS server is used. TTL is set as common for the zone and for all of
# its records with a default value of 14400 seconds.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
ip=$3
ns1=$4
ns2=$5
ns3=$6
ns4=$7
ns5=$8
ns6=$9
ns7=${10}
ns8=${11}
restart=${12}
dnssec=${13}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
domain_utf=$(idn2 --quiet -d "$domain_idn")
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN IP [NS1] [NS2] [NS3] [..] [NS8] [RESTART]'
is_format_valid 'user' 'domain' 'ip'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
if [ "$($BIN/v-list-dns-domain $user $domain_utf plain | cut -f 1) " != "$domain" ]; then
is_domain_new 'dns' "$domain_utf"
fi
if [ "$($BIN/v-list-dns-domain $user $domain_idn plain | cut -f 1) " != "$domain" ]; then
is_domain_new 'dns' "$domain_idn"
else
is_domain_new 'dns' "$domain"
fi
if [ -z "$(is_ip_format_valid $domain)" ]; then
echo "Error: Invalid domain format. IP address detected as input."
exit 1
fi
if [ -n "$restart" ]; then
is_format_valid 'restart'
fi
if [ -n "$dnssec" ]; then
is_boolean_format_valid "$dnssec" 'dnssec'
fi
is_package_full 'DNS_DOMAINS'
template=$(get_user_value '$DNS_TEMPLATE')
is_dns_template_valid "$template"
is_base_domain_owner "$domain"
if [ -n "$ns1" ]; then
ns1=$(echo $4 | sed -e 's/\.*$//g' -e 's/^\.*//g')
is_format_valid 'ns1'
fi
if [ -n "$ns2" ]; then
ns2=$(echo $5 | sed -e 's/\.*$//g' -e 's/^\.*//g')
is_format_valid 'ns2'
fi
if [ -n "$ns3" ]; then
ns3=$(echo $6 | sed -e 's/\.*$//g' -e 's/^\.*//g')
is_format_valid 'ns3'
fi
if [ -n "$ns4" ]; then
ns4=$(echo $7 | sed -e 's/\.*$//g' -e 's/^\.*//g')
is_format_valid 'ns4'
fi
if [ -n "$ns5" ]; then
ns5=$(echo $8 | sed -e 's/\.*$//g' -e 's/^\.*//g')
is_format_valid 'ns5'
fi
if [ -n "$ns6" ]; then
ns6=$(echo $9 | sed -e 's/\.*$//g' -e 's/^\.*//g')
is_format_valid 'ns6'
fi
if [ -n "$ns7" ]; then
ns7=$(echo ${10} | sed -e 's/\.*$//g' -e 's/^\.*//g')
is_format_valid 'ns7'
fi
if [ -n "$ns8" ]; then
ns8=$(echo ${11} | sed -e 's/\.*$//g' -e 's/^\.*//g')
is_format_valid 'ns8'
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Defining NS variables
if [ -z $ns2 ]; then
i=1
ns=$(get_user_value '$NS')
for nameserver in ${ns//,/ }; do
eval ns$i=$nameserver
((++i))
done
fi
soa="$ns1"
exp=$(date +%F -d "+ 1 year")
serial=$(date +'%Y%m%d01')
ttl=14400
# Reading template
template_data=$(cat "$DNSTPL/$template.tpl")
# Deleting unused nameservers
if [ -z "$ns3" ]; then
template_data=$(echo "$template_data" | grep -v %ns3%)
fi
if [ -z "$ns4" ]; then
template_data=$(echo "$template_data" | grep -v %ns4%)
fi
if [ -z "$ns5" ]; then
template_data=$(echo "$template_data" | grep -v %ns5%)
fi
if [ -z "$ns6" ]; then
template_data=$(echo "$template_data" | grep -v %ns6%)
fi
if [ -z "$ns7" ]; then
template_data=$(echo "$template_data" | grep -v %ns7%)
fi
if [ -z "$ns8" ]; then
template_data=$(echo "$template_data" | grep -v %ns8%)
fi
if [ -z "$dnssec" ]; then
dnssec="no"
fi
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Adding dns zone to the user config
echo "$template_data" \
| sed -e "s/%ip%/$ip/g" \
-e "s/%domain_idn%/$domain_idn/g" \
-e "s/%domain%/$domain/g" \
-e "s/%ns1%/$ns1/g" \
-e "s/%ns2%/$ns2/g" \
-e "s/%ns3%/$ns3/g" \
-e "s/%ns4%/$ns4/g" \
-e "s/%ns5%/$ns5/g" \
-e "s/%ns6%/$ns6/g" \
-e "s/%ns7%/$ns7/g" \
-e "s/%ns8%/$ns8/g" \
-e "s/%time%/$time/g" \
-e "s/%date%/$date/g" > $USER_DATA/dns/$domain.conf
chmod 660 $USER_DATA/dns/$domain.conf
records="$(wc -l $USER_DATA/dns/$domain.conf | cut -f 1 -d ' ')"
# Adding dns.conf record
dns_rec="DOMAIN='$domain' IP='$ip' TPL='$template' TTL='$ttl' EXP='$exp'"
dns_rec="$dns_rec SOA='$soa' SERIAL='$serial' SRC='' RECORDS='$records'"
dns_rec="$dns_rec DNSSEC='$dnssec' KEY='' SLAVE='no' MASTER='' SUSPENDED='no' TIME='$time' DATE='$date'"
echo "$dns_rec" >> $USER_DATA/dns.conf
chmod 660 $USER_DATA/dns.conf
rebuild_dns_domain_conf
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
cmd="$BIN/v-add-remote-dns-domain $user $domain yes"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Increasing domain value
increase_user_value "$user" '$U_DNS_DOMAINS'
increase_user_value "$user" '$U_DNS_RECORDS' "$records"
# Restart named
$BIN/v-restart-dns $restart
check_result $? "DNS restart failed"
# Logging
$BIN/v-log-action "$user" "Info" "DNS" "Added new DNS domain (Name: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,91 @@
#!/bin/bash
# info: add dns domain or dns record after web domain alias
# options: USER ALIAS IP [RESTART]
#
# example: v-add-dns-on-web-alias admin www.example.com 8.8.8.8
#
# This function adds dns domain or dns record based on web domain alias.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
alias=$2
ip=$3
restart=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER ALIAS IP [RESTART]'
is_format_valid 'user' 'alias' 'ip' 'restart'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
if [ -e "$USER_DATA/dns/$alias.conf" ]; then
exit
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Logging
log_event "$OK" "$ARGUMENTS"
# Define additional vars
sub_domain=$(echo "$alias" | awk -F '.' '{print $1}')
top_domain=$(echo "$alias" | sed -e "s/^$sub_domain.//")
domain_lvl=$(echo "$alias" | grep -o "\." | wc -l)
# Adding second level domain
if [ "$domain_lvl" -eq 1 ] || [ "${#top_domain}" -le '6' ]; then
$BIN/v-add-dns-domain \
"$user" "$alias" "$ip" '' '' '' '' '' '' '' '' "$restart" >> /dev/null
exit
fi
# Adding top-level domain and then its sub
$BIN/v-add-dns-domain "$user" "$top_domain" "$ip" '' '' '' '' '' '' '' '' "$restart" >> /dev/null
# Checking top-level domain
if [ ! -e "$USER_DATA/dns/$top_domain.conf" ]; then
exit
fi
# Checking subdomain record
if [ "$sub_domain" == '*' ]; then
check_record=$(grep -w "RECORD='\*'" $USER_DATA/dns/$top_domain.conf)
else
check_record=$(grep -w "RECORD='$sub_domain'" $USER_DATA/dns/$top_domain.conf)
fi
# Adding subdomain record
if [ -z "$check_record" ]; then
$BIN/v-add-dns-record \
"$user" "$top_domain" "$sub_domain" A "$ip" '' '' "$restart" >> /dev/null
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# No logging
exit

@ -0,0 +1,172 @@
#!/bin/bash
# info: add dns record
# options: USER DOMAIN RECORD TYPE VALUE [PRIORITY] [ID] [RESTART] [TTL]
#
# example: v-add-dns-record admin acme.com www A 162.227.73.112
#
# This function is used to add a new DNS record. Complex records of TXT, MX and
# SRV types can be used by a filling in the 'value' argument. This function also
# gets an ID parameter for definition of certain record identifiers or for the
# regulation of records.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
record=$(idn2 --quiet -d "$3")
record=$(echo "$record" | tr '[:upper:]' '[:lower:]')
rtype=$(echo "$4" | tr '[:lower:]' '[:upper:]')
dvalue=$5
priority=$6
id=$7
restart=$8
ttl=$9
quiet=${10}
if [ -z "$priority" ]; then
priority=10
fi
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Null priority for none MX/SRV records
if [ "$rtype" != 'MX' ] && [ "$rtype" != 'SRV' ]; then
priority=''
fi
# Add trailing dot at the end of NS/CNAME/MX/PTR/SRV record
if [[ $rtype =~ ^NS|CNAME|MX|PTR|SRV ]]; then
trailing_dot=$(echo $dvalue | grep "\.$")
if [ -z "$trailing_dot" ]; then
dvalue="$dvalue."
fi
fi
if [[ $rtype =~ ^NS|CNAME|MX|PTR|SRV ]]; then
dvalue=$(idn2 --quiet "$dvalue")
record=$(idn2 --quiet "$record")
fi
# Cleanup quotes on dvalue
# - [CAA] records will be left unchanged
# - [SRV] will be stripped of double quotes even when containing spaces
# - [DNSKEY] will be stripped of double quotes even when containing spaces
# - Rest of record types will be striped of quotes and the final string
# will be enclosed in double quotes if containing spaces or semicolons
if [ "$rtype" != "CAA" ]; then
dvalue=${dvalue//\"/}
# Add support for DS key
if [ "$rtype" != "DNSKEY" ] && [ "$rtype" != "DS" ]; then
if [ "$rtype" != 'SRV' ] && [[ "$dvalue" =~ [\;[:space:]] ]]; then
dvalue='"'"$dvalue"'"'
fi
fi
fi
if [ "$record" = "@" ] && [ "$rtype" = "CNAME" ]; then
check_result $E_INVALID "CNAME on root is not allowed"
fi
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '5' "$#" 'USER DOMAIN RECORD TYPE VALUE [PRIORITY] [ID] [RESTART] [TTL]'
is_format_valid 'user' 'domain' 'record' 'rtype' 'dvalue'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_object_unsuspended 'dns' 'DOMAIN' "$domain"
is_package_full 'DNS_RECORDS'
get_next_dnsrecord
is_format_valid 'id'
is_object_new "dns/$domain" 'ID' "$id"
is_dns_fqnd "$rtype" "$dvalue"
is_dns_nameserver_valid "$domain" "$rtype" "$dvalue"
is_format_valid 'ttl'
if [ -n "$restart" ]; then
is_format_valid 'restart'
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Adding record
zone="$USER_DATA/dns/$domain.conf"
dns_rec="ID='$id' RECORD='$record' TYPE='$rtype' PRIORITY='$priority'"
dns_rec="$dns_rec VALUE='$dvalue' SUSPENDED='no' TIME='$time' DATE='$date'"
[ -n "$ttl" ] && dns_rec="$dns_rec TTL='$ttl'"
echo "$dns_rec" >> $zone
chmod 660 $zone
# Sorting records
sort_dns_records
# Update serial
update_domain_serial
# Updating zone
if [[ "$DNS_SYSTEM" =~ named|bind ]]; then
# Do full rebuild due DNS SEC
rebuild_dns_domain_conf
fi
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
# Check for first sync
dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe)
if [ -z "$dlock" ]; then
cmd="$BIN/v-add-remote-dns-record $user $domain $id"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Update counters
records="$(wc -l $USER_DATA/dns/$domain.conf | cut -f1 -d ' ')"
update_object_value 'dns' 'DOMAIN' "$domain" '$RECORDS' "$records"
increase_user_value "$user" '$U_DNS_RECORDS'
# Restart named
$BIN/v-restart-dns "$restart"
check_result $? "$E_RESTART" 'dns failed to restart'
# Logging
if [ "$quiet" != "yes" ]; then
$BIN/v-log-action "$user" "Info" "DNS" "Added DNS record (Type: $rtype, Value: $record, Domain: $domain)."
fi
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,100 @@
#!/bin/bash
# info: add web/dns/mail domain
# options: USER DOMAIN [IP] [RESTART]
#
# example: v-add-domain admin example.com
#
# This function adds web/dns/mail domain to a server.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
ip=$3
restart=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN [IP] [RESTART]'
is_format_valid 'user' 'domain' 'restart'
if [ -n "$ip" ]; then
is_format_valid 'ip'
fi
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Get ip if it wasn't defined
if [ -z "$ip" ]; then
get_user_ip
if [ -z "$ip" ]; then
check_result "$E_NOTEXIST" "no available IP address"
fi
fi
# Working on web domain
if [ -n "$WEB_SYSTEM" ]; then
check1=$(is_package_full 'WEB_DOMAINS')
if [ $? -eq 0 ]; then
$BIN/v-add-web-domain "$user" "$domain" "$ip" 'no'
check_result $? "can't add web domain"
fi
fi
# Working on DNS domain
if [ -n "$DNS_SYSTEM" ]; then
check2=$(is_package_full 'DNS_DOMAINS')
if [ $? -eq 0 ]; then
$BIN/v-add-dns-domain "$user" "$domain" "$ip" "" "" "" "" "" "" "" "" "no"
check_result $? "can't add dns domain"
fi
fi
# Working on mail domain
if [ -n "$MAIL_SYSTEM" ]; then
check3=$(is_package_full 'MAIL_DOMAINS')
if [ $? -eq 0 ]; then
$BIN/v-add-mail-domain $user $domain 'no'
check_result $? "can't add mail domain"
fi
fi
if [[ "$check1" != '' && "$check2" != '' && "$check3" != '' ]]; then
check_result 8 "Package limit reached"
fi
# Restarting services
$BIN/v-restart-web "$restart"
check_result $? "can't restart web" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "can't restart proxy" > /dev/null
$BIN/v-restart-dns "$restart"
check_result $? "can't restart dns" > /dev/null
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
exit

@ -0,0 +1,118 @@
#!/bin/bash
# info: Enable FastCGI cache for nginx
# options: USER DOMAIN [DURATION] [RESTART]
#
# example: v-add-fastcgi-cache user domain.tld 30m
#
# This function enables FastCGI cache for nginx
# Acceptable values for duration is time in seconds (10s) minutes (10m) or days (10d)
# Add "yes" as last parameter to restart nginx
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
duration=${3-2m}
restart=${4-no}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN [DURATION] [RESTART]'
is_format_valid 'user' 'domain' 'restart'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
if ! [[ "$duration" =~ ^[0-9].*[s|m|d]$ ]]; then
echo "Invalid duration"
exit 2
fi
if [[ "$duration" =~ ^[0].*[s|m|d]$ ]]; then
echo "Invalid duration"
exit 2
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Load domain data
parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/web.conf)
# Check that nginx is not in proxy mode
if [ "$WEB_SYSTEM" != 'nginx' ]; then
echo "Error: nginx is in proxy mode"
exit "$E_NOTEXIST"
fi
fastcgi="$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.fastcgi_cache.conf"
no_cache='$no_cache'
cat << EOF > $fastcgi
fastcgi_cache $domain;
fastcgi_cache_valid 200 $duration;
fastcgi_cache_valid 301 302 10m;
fastcgi_cache_valid 404 10m;
fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;
set $no_cache 0;
EOF
chown root:$user $fastcgi
chmod 640 $fastcgi
str="fastcgi_cache_path /var/cache/nginx/micro/$domain levels=1:2"
str="$str keys_zone=$domain:10m max_size=512m inactive=30m use_temp_path=off;"
conf='/etc/nginx/conf.d/fastcgi_cache_pool.conf'
if [ -f "$conf" ]; then
if [ -z "$(grep "=${domain}:" $conf)" ]; then
echo "$str" >> $conf
fi
else
echo "$str" >> $conf
fi
mkdir -p /var/cache/nginx/micro/$domain
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
if [ -z "$FASTCGI_CACHE" ]; then
add_object_key "web" 'DOMAIN' "$domain" 'FASTCGI_CACHE' 'ALIAS'
fi
if [ -z "$FASTCGI_DURATION" ]; then
add_object_key "web" 'DOMAIN' "$domain" 'FASTCGI_DURATION' 'ALIAS'
fi
# Set FastCGI cache flag to enabled
update_object_value 'web' 'DOMAIN' "$domain" '$FASTCGI_CACHE' 'yes'
update_object_value 'web' 'DOMAIN' "$domain" '$FASTCGI_DURATION' "$duration"
# Restart web server
$BIN/v-restart-web "$restart"
check_result $? "Web server restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "Web" "FastCGI cache enabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,91 @@
#!/bin/bash
# info: add firewall blocking rule
# options: IP CHAIN
#
# example: v-add-firewall-ban 37.120.129.20 MAIL
#
# This function adds new blocking rule to system firewall
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
ip=$1
chain=$(echo $2 | tr '[:lower:]' '[:upper:]')
# Defining absolute path for iptables and modprobe
iptables="/sbin/iptables"
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/firewall.sh
source $HESTIA/func/firewall.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'IP CHAIN'
is_format_valid 'ip' 'chain'
is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Self heal iptables links
heal_iptables_links
# Checking server ip
if [ -e "$HESTIA/data/ips/$ip" ] || [ "$ip" = '127.0.0.1' ]; then
exit
fi
# Checking ip exclusions
excludes="$HESTIA/data/firewall/excludes.conf"
check_excludes=$(grep "^$ip$" $excludes 2> /dev/null)
if [ -n "$check_excludes" ]; then
exit
fi
# Checking ip in banlist
conf="$HESTIA/data/firewall/banlist.conf"
check_ip=$(grep "IP='$ip' CHAIN='$chain'" $conf 2> /dev/null)
if [ -n "$check_ip" ]; then
exit
fi
# Adding chain
$BIN/v-add-firewall-chain $chain
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Adding ip to banlist
echo "IP='$ip' CHAIN='$chain' TIME='$time' DATE='$date'" >> $conf
$iptables -I fail2ban-$chain 1 -s $ip \
-j REJECT --reject-with icmp-port-unreachable 2> /dev/null
# Changing permissions
chmod 660 $conf
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Warning" "Firewall" "Banned IP address $ip."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,130 @@
#!/bin/bash
# info: add firewall chain
# options: CHAIN [PORT] [PROTOCOL] [PROTOCOL]
#
# example: v-add-firewall-chain CRM 5678 TCP
#
# This function adds new rule to system firewall
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
chain=$(echo $1 | tr '[:lower:]' '[:upper:]')
port=$2
port_ext=$2
protocol=${4-TCP}
protocol=$(echo $protocol | tr '[:lower:]' '[:upper:]')
# Defining absolute path to iptables
iptables="/sbin/iptables"
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/firewall.sh
source $HESTIA/func/firewall.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Get hestia port by reading nginx.conf
hestiaport=$(grep -m 1 'listen' $HESTIA/nginx/conf/nginx.conf | awk '{print $2}' | sed "s|;||")
if [ -z "$hestiaport" ]; then
hestiaport=8083
fi
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'CHAIN [PORT] [PROTOCOL]'
is_format_valid 'chain' 'port_ext' 'protocol'
is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Self heal iptables links
heal_iptables_links
# Checking known chains
case $chain in
SSH) # Get ssh port by reading ssh config file.
sshport=$(grep '^Port ' /etc/ssh/sshd_config | head -1 | cut -d ' ' -f 2)
if [ -z "$sshport" ]; then
sshport=22
fi
port=$sshport
protocol=TCP
;;
FTP)
port=21
protocol=TCP
;;
MAIL)
port='25,465,587,110,995,143,993'
protocol=TCP
;;
DNS)
port=53
protocol=UDP
;;
WEB)
port='80,443'
protocol=TCP
;;
DB)
port='3306,5432'
protocol=TCP
;;
HESTIA)
port=$hestiaport
protocol=TCP
;;
RECIDIVE)
port='1:65535'
protocol=TCP
;;
*) check_args '2' "$#" 'CHAIN PORT' ;;
esac
# Adding chain
$iptables -N fail2ban-$chain 2> /dev/null
if [ $? -eq 0 ]; then
$iptables -A fail2ban-$chain -j RETURN
# Adding multiport module
if [[ "$port" =~ ,|-|: ]]; then
port_str="-m multiport --dports $port"
else
port_str="--dport $port"
fi
$iptables -I INPUT -p $protocol $port_str -j fail2ban-$chain
fi
# Preserving chain
chains=$HESTIA/data/firewall/chains.conf
check_chain=$(grep "CHAIN='$chain'" $chains 2> /dev/null)
if [ -z "$check_chain" ]; then
echo "CHAIN='$chain' PORT='$port' PROTOCOL='$protocol'" >> $chains
fi
# Changing permissions
chmod 660 $chains
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Info" "Firewall" "Added service to firewall (Service: $chain, Port: $port, Protocol: $protocol)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,205 @@
#!/bin/bash
# info: add firewall ipset
# options: NAME [SOURCE] [IPVERSION] [AUTOUPDATE] [REFRESH]
#
# example: v-add-firewall-ipset country-nl "https://raw.githubusercontent.com/ipverse/rir-ip/master/country/nl/ipv4-aggregated.txt"
#
# This function adds new ipset to system firewall
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
ip_name=${1}
data_source=${2}
ip_version=${3:-v4}
autoupdate=${4:-yes}
refresh=${5:-no}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'NAME [SOURCE] [IPVERSION] [AUTOUPDATE] [FORCE]'
is_format_valid 'ip_name'
is_boolean_format_valid "$autoupdate" 'Automatically update IP list (yes/no)'
is_boolean_format_valid "$refresh" 'Refresh IP list (yes/no)'
is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
# Define variables for ipset configuration
ipset_hstobject='../../data/firewall/ipset'
IPSET_BIN="$(command -v ipset)"
IPSET_PATH="$HESTIA/data/firewall/ipset"
# Ensure ipset is installed
if [ -z "$IPSET_BIN" ]; then
if [ -f '/etc/redhat-release' ]; then
dnf install -q -y ipset > /dev/null
else
apt-get --quiet --yes install ipset > /dev/null
fi
check_result $? "Installing IPset package"
IPSET_BIN="$(which ipset)"
check_result $? "IPset binary not found"
fi
# Ensure ipset configuration path and master file exist before attempting to parse
mkdir -p "$IPSET_PATH"
if [ ! -f "$HESTIA/data/firewall/ipset.conf" ]; then
touch $HESTIA/data/firewall/ipset.conf
fi
if [ -z "$data_source" ]; then
if [ ! -f "${IPSET_PATH}.conf" ] || [[ ! $(grep "LISTNAME='$ip_name'" "${IPSET_PATH}.conf") ]]; then
check_args '2' "$#" 'NAME SOURCE [IPVERSION] [AUTOUPDATE] [FORCE]'
fi
data_source="$(get_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$SOURCE')"
ip_version="$(get_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$IP_VERSION')"
else
is_object_new "$ipset_hstobject" 'LISTNAME' "$ip_name"
fi
if [ "$ip_version" != "v4" ] && [ "$ip_version" != "v6" ]; then
check_result "$E_INVALID" "invalid ip version, valid: (v4|v6)"
fi
if ! echo "$data_source" | egrep -q '^(https?|script|file):'; then
check_result "$E_INVALID" "invalid ipset source, valid: (http[s]://|script:|file:)"
fi
IPSET_FILE="${ip_name}.${ip_version}"
IPSET_MIN_SIZE=10
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generate ip lists file if missing or required refresh
if [ ! -f "${IPSET_PATH}/${IPSET_FILE}.iplist" ] || [ "$refresh" = "yes" ]; then
iplist_tempfile=$(mktemp)
if [[ "$data_source" =~ ^https?:// ]]; then
wget --tries=3 --timeout=15 --read-timeout=15 --waitretry=3 --no-dns-cache --quiet "$data_source" -O "$iplist_tempfile"
check_result $? "Downloading ip list"
# Advanced: execute script with the same basename for aditional pre-processing
# ex:
if [ -x "${IPSET_PATH}/${IPSET_FILE}.sh" ]; then
if [ -e '/etc/redhat-release' ]; then
preprocess_output="$(cat "$iplist_tempfile" | setpriv --clear-groups --reuid nobody --regid nobody -- ${IPSET_PATH}/${IPSET_FILE}.sh "$ip_name" "$iplist_tempfile")"
else
preprocess_output="$(cat "$iplist_tempfile" | setpriv --clear-groups --reuid nobody --regid nogroup -- ${IPSET_PATH}/${IPSET_FILE}.sh "$ip_name" "$iplist_tempfile")"
fi
check_result $? "Preprocessing script failed (${IPSET_FILE}.sh)"
[[ "$preprocess_output" ]] && echo "$preprocess_output" > "$iplist_tempfile"
fi
elif [[ "$data_source" =~ ^script:/ ]]; then
# Generate the ip list file trough a external script
# ex: compiling a ip list from multiple sources on demand
if [ -x "${data_source#script:}" ]; then
if [ -e '/etc/redhat-release' ]; then
setpriv --clear-groups --reuid nobody --regid nobody -- ${data_source#script:} "$ip_name" > "$iplist_tempfile"
else
setpriv --clear-groups --reuid nobody --regid nogroup -- ${data_source#script:} "$ip_name" > "$iplist_tempfile"
fi
check_result $? "Running custom ip list update script"
fi
elif [[ "$data_source" =~ ^file:/ ]]; then
# Use a external ip-list file managed by other apps
# ex: Using a ip list that is continuously updated
[ -f "${data_source#file:}" ] && cp -f "${data_source#file:}" "$iplist_tempfile"
fi
# Cleanup ip list
sed -r -i -e 's/[;#].*$//' -e 's/[ \t]*$//' -e '/^$/d' "$iplist_tempfile"
if [[ $ip_version == 'v4' ]]; then
sed -i -r -n -e '/^((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])/p' "$iplist_tempfile"
elif [[ $ip_version == 'v6' ]]; then
sed -i -r -n -e '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}/p' "$iplist_tempfile"
fi
# Validate iplist file size
iplist_size=$(sed -r -e '/^#|^$/d' "$iplist_tempfile" | wc -l)
[[ "$iplist_size" -le "$IPSET_MIN_SIZE" ]] && check_result "$E_INVALID" "IP list file too small (<${IPSET_MIN_SIZE}), ignoring"
mv -f "$iplist_tempfile" "${IPSET_PATH}/${IPSET_FILE}.iplist"
fi
# Load ipset in kernel
inet_ver="inet"
[ "$ip_version" == "v6" ] && inet_ver="inet6"
$IPSET_BIN -quiet create -exist "$ip_name" hash:net family $inet_ver
$IPSET_BIN -quiet destroy "${ip_name}-tmp"
$IPSET_BIN create "${ip_name}-tmp" -exist hash:net family $inet_ver maxelem 1048576
$IPSET_BIN flush "${ip_name}-tmp"
sed -rn -e '/^#|^$/d' -e "s/^(.*)/add ${ip_name}-tmp \\1/p" "${IPSET_PATH}/${IPSET_FILE}.iplist" | $IPSET_BIN -quiet restore
check_result $? "Populating ipset table"
$IPSET_BIN swap "${ip_name}-tmp" "${ip_name}"
$IPSET_BIN -quiet destroy "${ip_name}-tmp"
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
if [ ! -f "${IPSET_PATH}.conf" ] || [ -z "$(get_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$LISTNAME')" ]; then
# Concatenating rule
str="LISTNAME='$ip_name' IP_VERSION='$ip_version' SOURCE='$data_source'"
str="$str AUTOUPDATE='$autoupdate' SUSPENDED='no'"
str="$str TIME='$time' DATE='$date'"
echo "$str" >> $HESTIA/data/firewall/ipset.conf
log_type="added"
elif [ "$refresh" = "yes" ]; then
# Update iplist last regen time
update_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$TIME' "$time"
update_object_value "$ipset_hstobject" 'LISTNAME' "$ip_name" '$DATE' "$date"
log_type="refreshed"
fi
# Changing permissions
chmod 660 $HESTIA/data/firewall/ipset.conf
chmod 660 "${IPSET_PATH}/${IPSET_FILE}.iplist"
# Install ipset daily cron updater
if ! grep --silent --no-messages "v-update-firewall-ipset" $HESTIA/data/queue/daily.pipe; then
cmd="$BIN/v-update-firewall-ipset yes"
echo "$cmd" >> $HESTIA/data/queue/daily.pipe
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Info" "Firewall" "IPset IP list ${log_type:-loaded} (Name: $ip_name, IP version: $ip_version, Autoupdate: $autoupdate)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,109 @@
#!/bin/bash
# info: add firewall rule
# options: ACTION IP PORT [PROTOCOL] [COMMENT] [RULE]
#
# example: v-add-firewall-rule DROP 185.137.111.77 25
#
# This function adds new rule to system firewall
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
action=$(echo $1 | tr '[:lower:]' '[:upper:]')
ip=$2
port_ext=$3
protocol=${4-TCP}
protocol=$(echo $protocol | tr '[:lower:]' '[:upper:]')
comment=$5
rule=$6
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Get next firewall rule id
get_next_fw_rule() {
if [ -z "$rule" ]; then
curr_str=$(grep "RULE=" $HESTIA/data/firewall/rules.conf \
| cut -f 2 -d \' | sort -n | tail -n1)
rule="$((curr_str + 1))"
fi
}
sort_fw_rules() {
cat $HESTIA/data/firewall/rules.conf \
| sort -n -k 2 -t \' > $HESTIA/data/firewall/rules.conf.tmp
mv -f $HESTIA/data/firewall/rules.conf.tmp \
$HESTIA/data/firewall/rules.conf
}
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'ACTION IP PORT [PROTOCOL] [COMMENT] [RULE]'
is_format_valid 'action' 'protocol' 'port_ext'
is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM'
get_next_fw_rule
is_format_valid 'rule'
is_object_new '../../data/firewall/rules' 'RULE' "$rule"
if [ -n "$comment" ]; then
is_format_valid 'comment'
fi
if [[ "$ip" =~ ^ipset: ]]; then
ipset_name="${ip#ipset:}"
$BIN/v-list-firewall-ipset plain | grep "^$ipset_name\s" > /dev/null
check_result $? 'ipset object not found' "$E_NOTEXIST"
else
is_format_valid 'ip'
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Concatenating rule
str="RULE='$rule' ACTION='$action' PROTOCOL='$protocol' PORT='$port_ext'"
str="$str IP='$ip' COMMENT='$comment' SUSPENDED='no'"
str="$str TIME='$time' DATE='$date'"
# Adding to config
echo "$str" >> $HESTIA/data/firewall/rules.conf
# Changing permissions
chmod 660 $HESTIA/data/firewall/rules.conf
# Sorting firewall rules by id number
sort_fw_rules
# Updating system firewall
$BIN/v-update-firewall
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Fix missing port value in log if zero
if [ -z "$port" ]; then
port="0"
fi
# Logging
$BIN/v-log-action "system" "Info" "Firewall" "Added firewall rule (Action: $action, Port: $port, Protocol: $protocol)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,85 @@
#!/bin/bash
# info: archive directory
# options: USER ARCHIVE SOURCE [SOURCE...]
#
# example: v-add-fs-archive admin archive.tar readme.txt
#
# This function creates tar archive
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
user=$1
archive=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER ARCHIVE FILE [FILE_2] [FILE_3] [FILE ...]'
is_format_valid 'user'
is_object_valid 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
# Checking user homedir
homedir=$(grep "^$user:" /etc/passwd | cut -f 6 -d :)
if [ -z "$homedir" ]; then
echo "Error: user home directory doesn't exist"
exit 12
fi
# Checking archive
if [ -e "$archive" ]; then
echo "Error: archive already exist $archive"
exit 1
fi
# Checking source path
IFS=$'\n'
i=1
for src in $*; do
if [ "$i" -gt 2 ]; then
rpath=$(readlink -f "$src")
if [ -z "$(echo $rpath | egrep "^/tmp|^$homedir")" ]; then
echo "Error: invalid source path $src"
exit 1
fi
fi
((i++))
done
i=1
for src in $*; do
if [ "$i" -gt 2 ]; then
# Deleting leading home path
src=$(echo "$src" | sed -e "s|/home/$user/||")
# Creating tar.gz archive
user_exec tar -rf "${archive/.gz/}" -C /home/$user $src > /dev/null 2>&1
if [ "$?" -ne 0 ]; then
echo "Error: archive $archive was not created"
exit 3
fi
fi
((i++))
done
# Checking gzip
if [[ "$archive" =~ \.gz$ ]]; then
user_exec gzip "${archive/.gz/}" > /dev/null 2>&1
if [ "$?" -ne 0 ]; then
echo "Error: archive $archive was not gziped"
exit 3
fi
fi
exit

@ -0,0 +1,55 @@
#!/bin/bash
# info: add directory
# options: USER DIRECTORY
#
# example: v-add-fs-directory admin mybar
#
# This function creates new directory on the file system
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
user=$1
dst_dir=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DIR'
is_format_valid 'user'
is_object_valid 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
# Checking user homedir
homedir=$(grep "^$user:" /etc/passwd | cut -f 6 -d :)
if [ -z "$homedir" ]; then
echo "Error: user home directory doesn't exist"
exit 12
fi
# Checking destination path
rpath=$(readlink -f "$dst_dir")
if [ -z "$(echo $rpath | egrep "^/tmp|^$homedir")" ]; then
echo "Error: invalid destination path $dst_dir"
exit 2
fi
# Adding directory
user_exec mkdir -p "$dst_dir" > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Error: directory $dst_dir was not created"
exit 3
fi
# Exiting
exit

@ -0,0 +1,55 @@
#!/bin/bash
# info: add file
# options: USER FILE
#
# example: v-add-fs-file admin readme.md
#
# This function creates new files on file system
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
user=$1
dst_file=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER FILE'
is_format_valid 'user'
is_object_valid 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
# Checking user homedir
homedir=$(grep "^$user:" /etc/passwd | cut -f 6 -d :)
if [ -z $homedir ]; then
echo "Error: user home directory doesn't exist"
exit 12
fi
# Checking destination path
rpath=$(readlink -f "$dst_file")
if [ -z "$(echo $rpath | egrep "^/tmp|^$homedir")" ]; then
echo "Error: invalid destination path $dst_file"
exit 2
fi
# Creating file
user_exec touch "$dst_file" > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Error: file $dst_file was not created"
exit 3
fi
# Exiting
exit

@ -0,0 +1,629 @@
#!/bin/bash
# info: check letsencrypt domain
# options: USER DOMAIN [ALIASES] [MAIL]
#
# example: v-add-letsencrypt-domain admin wonderland.com www.wonderland.com,demo.wonderland.com
# example: v-add-letsencrypt-domain admin wonderland.com '' yes
#
# This function check and validates domain with Let's Encrypt
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
aliases=$3
mail=${4// /}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# LE API
LE_API='https://acme-v02.api.letsencrypt.org'
if [[ "$LE_STAGING" = 'yes' ]]; then
LE_API='https://acme-staging-v02.api.letsencrypt.org'
fi
# encode base64
encode_base64() {
cat | base64 | tr '+/' '-_' | tr -d '\r\n='
}
# Let's Encrypt v2 curl function
query_le_v2() {
protected='{"nonce": "'$3'",'
protected=''$protected' "url": "'$1'",'
protected=''$protected' "alg": "RS256", "kid": "'$KID'"}'
content="Content-Type: application/jose+json"
payload_=$(echo -n "$2" | encode_base64)
protected_=$(echo -n "$protected" | encode_base64)
signature_=$(printf "%s" "$protected_.$payload_" \
| openssl dgst -sha256 -binary -sign $USER_DATA/ssl/user.key \
| encode_base64)
post_data='{"protected":"'"$protected_"'",'
post_data=$post_data'"payload":"'"$payload_"'",'
post_data=$post_data'"signature":"'"$signature_"'"}'
# Save http response to file passed as "$4" arg or print to stdout if not provided
# http response headers are always sent to stdout
local save_to_file=${4:-"/dev/stdout"}
curl --location --user-agent "HestiaCP" --insecure --retry 5 --retry-connrefused --silent --dump-header /dev/stdout --data "$post_data" "$1" --header "$content" --output "$save_to_file"
debug_log "API call" "exit status: $?"
}
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN [ALIASES] [MAIL]'
is_format_valid 'user' 'domain' 'aliases'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
if [ -n "$mail" ]; then
is_boolean_format_valid "$mail" 'mail'
fi
# Set DNS CAA record retrieval commands
if [ -n "$DNS_SYSTEM" ]; then
dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1)
caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "CAA" | grep -i "letsencrypt.org" | cut -d' ' -f1)
fi
if [ -z "$mail" ] || [ "$mail" = 'no' ]; then
mail=''
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
get_domain_values 'web'
# check if alias is the letsencrypt wildcard domain, if not, make the normal checks
if [[ "$aliases" != "*.$domain" ]]; then
for alias in $(echo "$aliases" | tr ',' '\n' | sort -u); do
check_alias="$(echo $ALIAS | tr ',' '\n' | grep ^$alias$)"
if [ -z "$check_alias" ]; then
check_result "$E_NOTEXIST" "domain alias $alias doesn't exist"
fi
done
fi
else
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
fi
# Dump debug info
debug_log() {
echo -e "\n==[${1}]==\n${2}\n" >> "$log_file"
}
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generate correct variables for mail domain SSL certificates
if [ -n "$mail" ]; then
root_domain=$domain
domain="mail.$root_domain"
webmail=$(get_object_value "mail" "DOMAIN" "$root_domain" '$WEBMAIL')
if [ -n "$webmail" ]; then
aliases="$WEBMAIL_ALIAS.$root_domain"
fi
else
parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/web.conf)
domain_redirect="$REDIRECT"
if [[ -n "$domain_redirect" ]]; then
domain_redirect_code="$REDIRECT_CODE"
$BIN/v-delete-web-domain-redirect $user $domain
fi
domain_forcessl="$SSL_FORCE"
if [[ "$domain_forcessl" == 'yes' ]]; then
$BIN/v-delete-web-domain-ssl-force $user $domain
fi
fi
log_file="/var/log/hestia/LE-${user}-${domain}.log"
touch "$log_file"
chmod 600 "$log_file"
echo -e "\n\n=============================
Date Time: $(date +%Y-%m-%d) $(date +%H:%M:%S)
WEB_SYSTEM: ${WEB_SYSTEM}
PROXY_SYSTEM: ${PROXY_SYSTEM}
user: ${user}
domain: ${domain}
" >> "$log_file"
# Registering LetsEncrypt user account
$BIN/v-add-letsencrypt-user $user
if [ "$?" -ne 0 ]; then
touch $HESTIA/data/queue/letsencrypt.pipe
sed -i "/ $domain /d" $HESTIA/data/queue/letsencrypt.pipe
send_notice "LETSENCRYPT" "Account registration failed ($user)"
check_result "$E_CONNECT" "LE account registration ($user)" > /dev/null
fi
# Parsing LetsEncrypt account data
source $USER_DATA/ssl/le.conf
# Checking wildcard alias
if [ "$aliases" = "*.$domain" ]; then
wildcard='yes'
proto="dns-01"
if [ ! -e "$HESTIA/data/users/$user/dns/$domain.conf" ]; then
check_result "$E_NOTEXIST" "DNS domain $domain doesn't exist"
fi
else
proto="http-01"
fi
echo -e "
- aliases: ${aliases}
- proto: ${proto}
- wildcard: ${wildcard}
" >> "$log_file"
# Check if dns records exist for requested domain/aliases
if [ "$proto" = "http-01" ]; then
for identifier in $(echo $domain,$aliases | tr ',' '\n' | sort -u); do
if [[ "$identifier" = *[![:ascii:]]* ]]; then
identifier=$(idn2 --quiet $identifier)
fi
if ! nslookup "${identifier}" > /dev/null 2>&1; then
# Attempt against Cloudflare DNS
if ! nslookup "${identifier}" 1.1.1.1 > /dev/null 2>&1; then
check_result "$E_NOTEXIST" "DNS record for $identifier doesn't exist"
fi
fi
done
fi
# Ensure DNS CAA record exists for Let's Encrypt before requesting certificate
if [ -n "$DNS_SYSTEM" ]; then
# Check for DNS zone
if [ "$dns_domain" = "$domain" ]; then
# Replace DNS domain CAA records with Let's Encrypt values
if [ -z "$caa_record" ]; then
$BIN/v-add-dns-record "$user" "$domain" '@' 'CAA' '0 issue "letsencrypt.org"'
else
$BIN/v-delete-dns-record "$user" "$domain" "$caa_record"
$BIN/v-add-dns-record "$user" "$domain" '@' 'CAA' '0 issue "letsencrypt.org"'
fi
fi
fi
# Requesting nonce / STEP 1
answer=$(curl --user-agent "HestiaCP" -s -I "$LE_API/directory")
nonce=$(echo "$answer" | grep -i nonce | cut -f2 -d \ | tr -d '\r\n')
status=$(echo "$answer" | grep HTTP/ | tail -n1 | cut -f 2 -d ' ')
debug_log "Step 1" "- status: ${status}\n- nonce: ${nonce}\n- answer: ${answer}"
if [[ "$status" -ne 200 ]]; then
# Delete DNS CAA record
if [ -n "$DNS_SYSTEM" ]; then
if [ "$dns_domain" = "$domain" ]; then
if [ -n "$caa_record" ]; then
$BIN/v-delete-dns-record "$user" "$domain" "$caa_record"
fi
fi
fi
check_result "$E_CONNECT" "Let's Encrypt nonce request status $status ($domain)"
fi
# Placing new order / STEP 2
url="$LE_API/acme/new-order"
payload='{"identifiers":['
for identifier in $(echo $domain,$aliases | tr ',' '\n' | sort -u); do
if [[ "$identifier" = *[![:ascii:]]* ]]; then
identifier=$(idn2 --quiet $identifier)
fi
payload=$payload'{"type":"dns","value":"'$identifier'"},'
done
payload=$(echo "$payload" | sed "s/,$//")
payload=$payload']}'
answer=$(query_le_v2 "$url" "$payload" "$nonce")
nonce=$(echo "$answer" | grep -i nonce | cut -f2 -d \ | tr -d '\r\n')
authz=$(echo "$answer" | grep "acme/authz" | cut -f2 -d '"')
finalize=$(echo "$answer" | grep 'finalize":' | cut -f4 -d '"')
status=$(echo "$answer" | grep HTTP/ | tail -n1 | cut -f2 -d ' ')
order=$(echo -e "$answer" | grep -i location | cut -f2 -d \ | tr -d '\r\n')
debug_log "Step 2" "- status: ${status}\n- nonce: ${nonce}\n- authz: ${authz}\n- finalize: ${finalize}\n- payload: ${payload}\n- answer: ${answer}\n order: ${order}"
if [[ "$status" -ne 201 ]]; then
# Delete DNS CAA record
if [ -n "$DNS_SYSTEM" ]; then
if [ "$dns_domain" = "$domain" ]; then
if [ -n "$caa_record" ]; then
$BIN/v-delete-dns-record "$user" "$domain" "$caa_record"
fi
fi
fi
check_result $E_CONNECT "Let's Encrypt new auth status $status ($domain)"
fi
# Requesting authorization token / STEP 3
for auth in $authz; do
payload=''
answer=$(query_le_v2 "$auth" "$payload" "$nonce")
url=$(echo "$answer" | grep -A3 $proto | grep -m1 url | cut -f 4 -d \")
token=$(echo "$answer" | grep -A3 $proto | grep token | cut -f 4 -d \")
nonce=$(echo "$answer" | grep -i nonce | cut -f2 -d \ | tr -d '\r\n')
status=$(echo "$answer" | grep HTTP/ | tail -n1 | cut -f 2 -d ' ')
debug_log "Step 3" "- status: ${status}\n- nonce: ${nonce}\n- url: ${url}\n- token: ${token}\n- answer: ${answer}"
if [[ "$status" -ne 200 ]]; then
# Delete DNS CAA record
if [ -n "$DNS_SYSTEM" ]; then
dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1)
caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1)
if [ "$dns_domain" = "$domain" ]; then
if [ -n "$caa_record" ]; then
$BIN/v-delete-dns-record "$user" "$domain" "$caa_record"
fi
fi
fi
check_result "$E_CONNECT" "Let's Encrypt acme/authz bad status $status ($domain)"
fi
# Accepting challenge / STEP 4
if [ "$wildcard" = 'yes' ]; then
record=$(printf "%s" "$token.$THUMB" \
| openssl dgst -sha256 -binary | encode_base64)
old_records=$($BIN/v-list-dns-records "$user" "$domain" plain | grep 'TXT')
old_records=$(echo "$old_records" | grep _acme-challenge | cut -f 1)
for old_record in $old_records; do
$BIN/v-delete-dns-record "$user" "$domain" "$old_record"
done
$BIN/v-add-dns-record "$user" "$domain" "_acme-challenge" "TXT" "$record"
check_result $? "DNS _acme-challenge record wasn't created ($domain)"
else
if [ -z "$mail" ]; then
if [ "$WEB_SYSTEM" = 'nginx' ] || [ "$PROXY_SYSTEM" = 'nginx' ]; then
conf="$HOMEDIR/$user/conf/web/$domain/nginx.conf_letsencrypt"
sconf="$HOMEDIR/$user/conf/web/$domain/nginx.ssl.conf_letsencrypt"
echo 'location ~ "^/\.well-known/acme-challenge/([-_A-Za-z0-9]+)$" {' \
> $conf
echo ' default_type text/plain;' >> $conf
echo ' return 200 "$1.'$THUMB'";' >> $conf
echo '}' >> $conf
if [ ! -e "$sconf" ]; then
ln -s "$conf" "$sconf"
fi
if [ -n "$PROXY_SYSTEM" ]; then
$BIN/v-restart-proxy
check_result $? "Proxy restart failed" > /dev/null
fi
else
# Get root directory from configuration
domain_config="$HOMEDIR/$user/conf/web/$domain"
if [ -f "$domain_config/apache2.conf" ]; then
well_known="$(cat $domain_config/apache2.conf | egrep \
'^\s+DocumentRoot' | awk '{split($0, a, " "); \
print a[2]}')/.well-known"
else
well_known="$(cat $domain_config/nginx.conf | egrep '^\s+root' \
| awk '{split($0, a, " "); print a[2]}' \
| sed 's/;$//')/.well-known"
fi
acme_challenge="$well_known/acme-challenge"
mkdir -p $acme_challenge
echo "$token.$THUMB" > $acme_challenge/$token
chown -R $user:$user $well_known
fi
else
if [ "$WEB_SYSTEM" = 'nginx' ] || [ "$PROXY_SYSTEM" = 'nginx' ]; then
conf="$HOMEDIR/$user/conf/mail/$root_domain/nginx.conf_letsencrypt"
sconf="$HOMEDIR/$user/conf/mail/$root_domain/nginx.ssl.conf_letsencrypt"
echo 'location ~ "^/\.well-known/acme-challenge/([-_A-Za-z0-9]+)$" {' \
> $conf
echo ' default_type text/plain;' >> $conf
echo ' return 200 "$1.'$THUMB'";' >> $conf
echo '}' >> $conf
if [ ! -e "$sconf" ]; then
ln -s "$conf" "$sconf"
fi
if [ -n "$PROXY_SYSTEM" ]; then
$BIN/v-restart-proxy
check_result $? "Proxy restart failed" > /dev/null
fi
else
get_object_value 'mail' 'DOMAIN' "$root_domain" "WEBMAIL"
if [ -n "$WEBMAIL" ]; then
well_known="/var/lib/$WEBMAIL/.well-known"
acme_challenge="$well_known/acme-challenge"
mkdir -p $acme_challenge
echo "$token.$THUMB" > $acme_challenge/$token
chown -R $user:$user $well_known
fi
fi
fi
if [ "$WEB_SYSTEM" = 'nginx' ]; then
$BIN/v-restart-web
check_result $? "Web restart failed" > /dev/null
fi
fi
if [ "$DNS_CLUSTER" = "yes" ]; then
$BIN/v-update-sys-queue dns-cluster
fi
# Requesting ACME validation / STEP 5
validation_check=$(echo "$answer" | grep '"valid"')
if [[ -n "$validation_check" ]]; then
validation='valid'
else
validation='pending'
sleep 5
fi
# Doing pol check on status
i=1
while [ "$validation" = 'pending' ]; do
payload='{}'
answer=$(query_le_v2 "$url" "$payload" "$nonce")
validation=$(echo "$answer" | grep -A1 $proto | tail -n1 | cut -f4 -d \")
nonce=$(echo "$answer" | grep -i nonce | cut -f2 -d \ | tr -d '\r\n')
status=$(echo "$answer" | grep HTTP/ | tail -n1 | cut -f 2 -d ' ')
details=$(echo "$answer" | grep detail | cut -f 1 -d ',' | cut -f 2-4 -d ':' | cut -f 2 -d '"')
debug_log "Step 5" "- status: ${status}\n- url: ${url}\n- nonce: ${nonce}\n- validation: ${validation}\n- details: ${details}\n- answer: ${answer}"
if [[ "$status" -ne 200 ]]; then
# Delete DNS CAA record
if [ -n "$DNS_SYSTEM" ]; then
dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1)
caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1)
if [ "$dns_domain" = "$domain" ]; then
if [ -n "$caa_record" ]; then
$BIN/v-delete-dns-record "$user" "$domain" "$caa_record"
fi
fi
fi
# Download debug info from LE server
result=$(wget -qO- $url)
debug_log "Debug information Step 5" "$result"
details=$(echo $result | jq '.error.detail')
error_code=$(echo $result | jq '.error.status')
debug_log "Abort Step 5" "=> Wrong status"
check_result "$E_CONNECT" "Let's Encrypt validation status $status ($domain). Details: $error_code:$details"
fi
i=$((i + 1))
if [ "$i" -gt 10 ]; then
# Delete DNS CAA record
if [ -n "$DNS_SYSTEM" ]; then
dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1)
caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1)
if [ "$dns_domain" = "$domain" ]; then
if [ -n "$caa_record" ]; then
$BIN/v-delete-dns-record "$user" "$domain" "$caa_record"
fi
fi
fi
debug_log "Abort Step 5" "=> Too many validation retries"
check_result "$E_CONNECT" "Let's Encrypt domain validation timeout ($domain)"
fi
sleep $((i * 2))
done
if [ "$validation" = 'invalid' ]; then
# Delete DNS CAA record
if [ -n "$DNS_SYSTEM" ]; then
dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1)
caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1)
if [ "$dns_domain" = "$domain" ]; then
if [ -n "$caa_record" ]; then
$BIN/v-delete-dns-record "$user" "$domain" "$caa_record"
fi
fi
fi
check_result "$E_CONNECT" "Let's Encrypt domain verification failed ($domain)"
fi
done
# Generating new ssl certificate
ssl_dir=$($BIN/v-generate-ssl-cert "$domain" "info@$domain" "US" "California" \
"San Francisco" "Hestia" "IT" "$aliases" | tail -n1 | awk '{print $2}')
# Sending CSR to finalize order / STEP 6
csr=$(openssl req -in $ssl_dir/$domain.csr -outform DER | encode_base64)
payload='{"csr":"'$csr'"}'
answer=$(query_le_v2 "$finalize" "$payload" "$nonce")
nonce=$(echo "$answer" | grep -i nonce | cut -f2 -d \ | tr -d '\r\n')
status=$(echo "$answer" | grep HTTP/ | tail -n1 | cut -f 2 -d ' ')
certificate=$(echo "$answer" | grep 'certificate":' | cut -f4 -d '"')
debug_log "Step 6" "- status: ${status}\n- nonce: ${nonce}\n- payload: ${payload}\n- certificate: ${certificate}\n- answer: ${answer}"
if [[ "$status" -ne 200 ]]; then
[ -d "$ssl_dir" ] && rm -rf "$ssl_dir"
check_result "$E_CONNECT" "Let's Encrypt finalize bad status $status ($domain)"
fi
if [ -z "$certificate" ]; then
validation="processing"
i=1
while [ "$validation" = "processing" ]; do
answer=$(query_le_v2 "$order" "" "$nonce")
i=$((i + 1))
nonce=$(echo "$answer" | grep -i nonce | cut -f2 -d \ | tr -d '\r\n')
status=$(echo "$answer" | grep HTTP/ | tail -n1 | cut -f 2 -d ' ')
validation=$(echo "$answer" | grep 'status":' | cut -f4 -d '"')
certificate=$(echo "$answer" | grep 'certificate":' | cut -f4 -d '"')
sleep $((i * 2)) # Sleep for 2s, 4s, 6s, 8s
if [ $i -gt 10 ]; then
check_result "$E_CONNECT" "Certificate processing timeout ($domain)"
fi
debug_log "Step 7" "- status: ${status}\n- nonce: ${nonce}\n- payload: ${payload}\n- certificate: ${certificate}\n- answer: ${answer}"
done
fi
# Downloading signed certificate / STEP 7
status=0
retry=0
while [[ $status != 200 && $retry -lt 3 ]]; do
answer=$(query_le_v2 "$certificate" "" "$nonce" "$ssl_dir/$domain.pem")
status=$(echo "$answer" | grep HTTP/ | tail -n1 | cut -f 2 -d ' ')
debug_log "Step 8" "- status: ${status}\n- retry: ${retry}\n- answer: ${answer}"
if [[ $status != 200 ]]; then
retry=$((retry + 1))
sleep $((retry * 2)) # Sleep for 2s, 4s, 6s, 8s
fi
done
# Fallback on depreciated download method for certs (unauthenticated GET)
if [[ $status != 200 ]]; then
answer=$(curl --insecure --user-agent "HestiaCP" --retry 5 --retry-connrefused --silent --dump-header /dev/stdout "$certificate" --output "$ssl_dir/$domain.pem")
status=$(echo "$answer" | grep HTTP/ | tail -n1 | cut -f 2 -d ' ')
debug_log "Step 8 - Fallback" "- status: ${status}\n- answer: ${answer}"
fi
debug_log "CERT DIR" "$(ls -las "$ssl_dir/")"
debug_log "CERT PEM" "$(cat "$ssl_dir/$domain.pem")"
if [[ "$status" -ne 200 ]]; then
[ -d "$ssl_dir" ] && rm -rf "$ssl_dir"
check_result "$E_NOTEXIST" "Let's Encrypt downloading signed cert failed status:$status ($domain)"
fi
# Splitting up downloaded pem
crt_end=$(grep -n 'END CERTIFICATE' $ssl_dir/$domain.pem | head -n1 | cut -f1 -d:)
head -n $crt_end $ssl_dir/$domain.pem > $ssl_dir/$domain.crt
pem_lines=$(wc -l $ssl_dir/$domain.pem | cut -f 1 -d ' ')
ca_end=$(grep -n 'BEGIN CERTIFICATE' $ssl_dir/$domain.pem | tail -n1 | cut -f 1 -d :)
ca_end=$((pem_lines - crt_end + 1))
tail -n $ca_end $ssl_dir/$domain.pem > $ssl_dir/$domain.ca
debug_log "CERT CRT" "$(cat "$ssl_dir/$domain.crt")"
debug_log "CERT CA-1" "$(cat "$ssl_dir/$domain.ca")"
# Temporary fix for double "END CERTIFICATE"
if [[ $(head -n 1 $ssl_dir/$domain.ca) = "-----END CERTIFICATE-----" ]]; then
sed -i '1,2d' $ssl_dir/$domain.ca
fi
debug_log "CERT CA-2" "$(cat "$ssl_dir/$domain.ca")"
# Rename certs for mail
if [ -n "$mail" ]; then
mv $ssl_dir/$domain.ca $ssl_dir/$root_domain.ca
mv $ssl_dir/$domain.crt $ssl_dir/$root_domain.crt
mv $ssl_dir/$domain.csr $ssl_dir/$root_domain.csr
mv $ssl_dir/$domain.key $ssl_dir/$root_domain.key
mv $ssl_dir/$domain.pem $ssl_dir/$root_domain.pem
fi
# Adding SSL
if [ -z "$mail" ]; then
ssl_home="$(get_object_value 'web' 'DOMAIN' "$domain" '$SSL_HOME')"
ssl_enabled="$(get_object_value 'web' 'DOMAIN' "$domain" '$SSL')"
if [ "$ssl_enabled" = "yes" ]; then
$BIN/v-update-web-domain-ssl "$user" "$domain" "$ssl_dir" "updatessl"
else
$BIN/v-add-web-domain-ssl "$user" "$domain" "$ssl_dir" "$ssl_home" "updatessl"
fi
else
# TODO replace with v-update-mail-domain-ssl if ssl is enabled
ssl_enabled="$(get_object_value 'mail' 'DOMAIN' "$root_domain" '$SSL')"
if [ "$ssl_enabled" = "yes" ]; then
$BIN/v-update-mail-domain-ssl "$user" "$root_domain" "$ssl_dir" "updatessl"
else
$BIN/v-add-mail-domain-ssl "$user" "$root_domain" "$ssl_dir" "updatessl"
fi
fi
if [ "$?" -ne '0' ]; then
[ -d "$ssl_dir" ] && rm -rf "$ssl_dir"
touch $HESTIA/data/queue/letsencrypt.pipe
sed -i "/ $domain /d" $HESTIA/data/queue/letsencrypt.pipe
send_notice 'LETSENCRYPT' "$domain certificate installation failed ($domain)"
check_result $? "SSL install" > /dev/null
fi
# Adding LE autorenew cronjob
if [ -z "$(grep v-update-lets $HESTIA/data/users/admin/cron.conf)" ]; then
min=$(generate_password '012345' '2')
hour=$(generate_password '1234567' '1')
cmd="sudo $BIN/v-update-letsencrypt-ssl"
$BIN/v-add-cron-job admin "$min" "$hour" '*' '*' '*' "$cmd" > /dev/null
fi
# Updating letsencrypt key
if [ -z "$mail" ]; then
if [ -z "$LETSENCRYPT" ]; then
add_object_key "web" 'DOMAIN' "$domain" 'LETSENCRYPT' 'FTP_USER'
add_object_key "web" 'DOMAIN' "$domain" 'LETSENCRYPT_FAIL_COUNT' 'LETSENCRYPT'
fi
update_object_value 'web' 'DOMAIN' "$domain" '$LETSENCRYPT' 'yes'
update_object_value 'web' 'DOMAIN' "$domain" '$LETSENCRYPT_FAIL_COUNT' "0"
if [[ "$domain_forcessl" == 'yes' ]]; then
$BIN/v-add-web-domain-ssl-force $user $domain
fi
if [[ -n "$domain_redirect" ]]; then
$BIN/v-add-web-domain-redirect $user $domain $domain_redirect $domain_redirect_code
fi
else
if [ -z "$LETSENCRYPT" ]; then
add_object_key "mail" 'DOMAIN' "$root_domain" 'LETSENCRYPT'
add_object_key "mail" 'DOMAIN' "$root_domain" 'LETSENCRYPT_FAIL_COUNT' 'LETSENCRYPT'
fi
update_object_value 'mail' 'DOMAIN' "$root_domain" '$LETSENCRYPT' 'yes'
update_object_value 'mail' 'DOMAIN' "$root_domain" '$LETSENCRYPT_FAIL_COUNT' "0"
fi
# Remove challenge folder if exist
if [ -n "$well_known" ]; then
rm -fr $well_known
fi
# Remove temporary SSL folder
[ -d "$ssl_dir" ] && rm -rf "$ssl_dir"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Deleting task from queue
touch $HESTIA/data/queue/letsencrypt.pipe
sed -i "/ $domain /d" $HESTIA/data/queue/letsencrypt.pipe
# Notifying user
send_notice 'LETSENCRYPT' "$domain SSL has been installed successfully"
# Logging
log_event "$OK" "$ARGUMENTS"
# Cleanup debug since the SSL was issues successfully
rm -f "$log_file"
exit

@ -0,0 +1,98 @@
#!/bin/bash
# info: add letsencrypt for host and backend
# options: NONE
#
# example: v-add-letsencrypt-host
#
# This function check and validates the backend certificate and generate
# a new let's encrypt certificate.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
# Argument definition
domain=$(hostname -f)
if [ -z $domain ]; then
domain=$HOSTNAME
fi
user="$($BIN/v-search-domain-owner "$domain" web)"
[[ -z "$user" ]] && user="admin"
USER_DATA=$HESTIA/data/users/$user
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
is_format_valid 'user' 'domain' 'aliases'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Check if hostname already exists as domain
if [ "$($BIN/v-list-web-domain $user $domain plain | cut -f 1)" != "$domain" ]; then
# Create web domain for hostname
$BIN/v-add-web-domain "$user" "$domain"
fi
# Validate web domain
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
get_domain_values 'web'
# Load domain data
parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/web.conf)
# Set ssl installation to yes
add_ssl="yes"
if [ "$SSL" = "yes" ]; then
# Valildate SSL Certificate
if [ -e "$USER_DATA/ssl/$domain.ca" ]; then
if openssl verify -CAfile <(openssl x509 -in $USER_DATA/ssl/$domain.ca) $USER_DATA/ssl/$domain.pem | grep -q "$domain.pem: OK"; then
add_ssl="no"
fi
else
if openssl verify $USER_DATA/ssl/$domain.pem | grep -q "$domain.pem: OK"; then
add_ssl="no"
fi
fi
fi
# Add let's encrypt ssl if needed
if [ "$add_ssl" = "yes" ]; then
# Add let's encrypt ssl
$BIN/v-add-letsencrypt-domain "$user" "$domain"
check_result $? "Let's Encrypt SSL creation failed" "$E_UPDATE"
fi
# Add certificate to backend
$BIN/v-update-host-certificate "$user" "$domain"
# Enable automatic ssl forward and hsts
$BIN/v-add-web-domain-ssl-force "$user" "$domain" > /dev/null 2>&1
$BIN/v-add-web-domain-ssl-hsts "$user" "$domain" > /dev/null 2>&1
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
exit

@ -0,0 +1,148 @@
#!/bin/bash
# info: register letsencrypt user account
# options: USER
#
# example: v-add-letsencrypt-user bob
#
# This function creates and register LetsEncrypt account
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# LE API
LE_API='https://acme-v02.api.letsencrypt.org'
if [[ "$LE_STAGING" = 'yes' ]]; then
LE_API='https://acme-staging-v02.api.letsencrypt.org'
fi
# encode base64
encode_base64() {
cat | base64 | tr '+/' '-_' | tr -d '\r\n='
}
# Let's Encrypt v2 curl function
query_le_v2() {
protected='{"nonce": "'$3'",'
protected=''$protected' "url": "'$1'",'
protected=''$protected' "alg": "RS256", "jwk": '$jwk'}'
content="Content-Type: application/jose+json"
payload_=$(echo -n "$2" | encode_base64)
protected_=$(echo -n "$protected" | encode_base64)
signature_=$(printf "%s" "$protected_.$payload_" \
| openssl dgst -sha256 -binary -sign $USER_DATA/ssl/user.key \
| encode_base64)
post_data='{"protected":"'"$protected_"'",'
post_data=$post_data'"payload":"'"$payload_"'",'
post_data=$post_data'"signature":"'"$signature_"'"}'
curl --user-agent "HestiaCP" -s -i -d "$post_data" "$1" -H "$content"
}
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'USER'
is_format_valid 'user'
is_object_valid 'user' 'USER' "$user"
if [ -e "$USER_DATA/ssl/le.conf" ]; then
source "$USER_DATA/ssl/le.conf"
fi
if [ -n "$KID" ]; then
exit
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Defining user email
if [[ -z "$EMAIL" ]]; then
EMAIL=$(get_user_value '$CONTACT')
fi
# Defining user agreement
agreement=''
# Generating user key
KEY="$USER_DATA/ssl/user.key"
if [ ! -e "$KEY" ]; then
openssl genrsa -out "$KEY" 4096 > /dev/null 2>&1
chmod 600 $KEY
fi
# Defining key exponent
if [ -z "$EXPONENT" ]; then
EXPONENT=$(openssl pkey -inform pem -in "$KEY" -noout -text_pub \
| grep Exponent: | cut -f 2 -d '(' | cut -f 1 -d ')' | sed -e 's/x//' \
| xxd -r -p | encode_base64)
fi
# Defining key modulus
if [ -z "$MODULUS" ]; then
MODULUS=$(openssl rsa -in "$KEY" -modulus -noout \
| sed -e 's/^Modulus=//' | xxd -r -p | encode_base64)
fi
# Defining JWK
jwk='{"e":"'$EXPONENT'","kty":"RSA","n":"'"$MODULUS"'"}'
# Defining key thumbnail
if [ -z "$THUMB" ]; then
THUMB="$(echo -n "$jwk" | openssl dgst -sha256 -binary | encode_base64)"
fi
# Requesting ACME nonce
nonce=$(curl -s -I "$LE_API/directory" | grep -i nonce | cut -f2 -d\ | tr -d '\r\n')
# Creating ACME account
url="$LE_API/acme/new-acct"
payload='{"termsOfServiceAgreed": true}'
answer=$(query_le_v2 "$url" "$payload" "$nonce")
kid=$(echo "$answer" | grep -i location: | cut -f2 -d ' ' | tr -d '\r')
# Checking answer status
status=$(echo "$answer" | grep HTTP/ | tail -n1 | cut -f2 -d ' ')
if [[ "${status:0:2}" -ne "20" ]]; then
check_result "$E_CONNECT" "Let's Encrypt acc registration failed $status"
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding le.conf
if [ ! -e "$USER_DATA/ssl/le.conf" ]; then
echo "EXPONENT='$EXPONENT'" > $USER_DATA/ssl/le.conf
echo "MODULUS='$MODULUS'" >> $USER_DATA/ssl/le.conf
echo "THUMB='$THUMB'" >> $USER_DATA/ssl/le.conf
echo "EMAIL='$EMAIL'" >> $USER_DATA/ssl/le.conf
echo "KID='$kid'" >> $USER_DATA/ssl/le.conf
chmod 660 $USER_DATA/ssl/le.conf
else
sed -i '/^KID=/d' $USER_DATA/ssl/le.conf
echo "KID='$kid'" >> $USER_DATA/ssl/le.conf
fi
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,139 @@
#!/bin/bash
# info: add mail domain account
# options: USER DOMAIN ACCOUNT PASSWORD [QUOTA]
#
# example: v-add-mail-account user example.com john P4$$vvOrD
#
# This function add new email account.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
account=$3
password=$4
HIDE=4
quota=${5-unlimited}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/syshealth.sh
source $HESTIA/func/syshealth.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
if [[ "$account" =~ [[:upper:]] ]]; then
account=$(echo "$account" | tr '[:upper:]' '[:lower:]')
fi
format_domain
format_domain_idn
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '4' "$#" 'USER DOMAIN ACCOUNT PASSWORD [QUOTA]'
is_format_valid 'user' 'domain' 'account'
if [ "$quota" != 'unlimited' ]; then
is_format_valid 'quota'
fi
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_package_full 'MAIL_ACCOUNTS'
is_mail_new "$account"
is_password_valid
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating hashed password
if [ -n "$(doveadm pw -l | grep BLF-CRYPT)" ]; then
set +H # disable ! style history substitution
md5="$(doveadm pw -s BLF-CRYPT -p "$password")"
elif [ -n "$(doveadm pw -l | grep ARGON2ID)" ]; then
# Fall back on Argon2id if bcrypt is not available
set +H # disable ! style history substitution
md5="$(doveadm pw -s ARGON2ID -p "$password")"
else
# Fall back on MD5 if neither bcrypt nor argon2id is available
salt=$(generate_password "$PW_MATRIX" "8")
md5="{MD5}$($BIN/v-generate-password-hash md5 $salt <<< $password)"
fi
# Adding account info into password file
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
if [ "$quota" = 'unlimited' ]; then
quota='0'
fi
str="$account:$md5:$user:mail::$HOMEDIR/$user:${quota}:userdb_quota_rule=*:storage=${quota}M"
echo $str >> $HOMEDIR/$user/conf/mail/$domain/passwd
userstr="$account:$account:$user:mail:$HOMEDIR/$user"
echo $userstr >> $HOMEDIR/$user/conf/mail/$domain/accounts
fi
# Create mail account folder (mailbox)
mkdir $HOMEDIR/$user/mail/$domain_idn/$account
chown $user:mail $HOMEDIR/$user/mail/$domain_idn/$account
chmod 700 $HOMEDIR/$user/mail/$domain_idn/$account
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
if [[ "$quota" -eq '0' ]]; then
quota='unlimited'
fi
str="ACCOUNT='$account' ALIAS='' AUTOREPLY='no' FWD='' FWD_ONLY=''"
str="$str MD5='$md5' QUOTA='$quota' U_DISK='0' SUSPENDED='no'"
str="$str TIME='$time' DATE='$date'"
echo "$str" >> $USER_DATA/mail/$domain.conf
chmod 660 $USER_DATA/mail/$domain.conf
syshealth_repair_mail_account_config
touch $HOMEDIR/$user/conf/mail/$domain/limits
user_rate_limit=$(get_object_value 'mail' 'DOMAIN' "$domain" '$RATE_LIMIT')
if [ -n "$user_rate_limit" ]; then
sed -i "/^$account@$domain_idn:/ d" $HOMEDIR/$user/conf/mail/$domain/limits
echo "$account@$domain_idn:$user_rate_limit" >> $HOMEDIR/$user/conf/mail/$domain/limits
else
system=$(cat /etc/exim4/limit.conf)
sed -i "/^$account@$domain_idn:/ d" $HOMEDIR/$user/conf/mail/$domain/limits
echo "$account@$domain_idn:$system" >> $HOMEDIR/$user/conf/mail/$domain/limits
fi
# Increase mail accounts counter
accounts=$(wc -l $USER_DATA/mail/$domain.conf | cut -f 1 -d ' ')
increase_user_value "$user" '$U_MAIL_ACCOUNTS'
update_object_value 'mail' 'DOMAIN' "$domain" '$ACCOUNTS' "$accounts"
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "Added new mail account ($account@$domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,80 @@
#!/bin/bash
# info: add mail account alias aka nickname
# options: USER DOMAIN ACCOUNT ALIAS
#
# example: v-add-mail-account-alias admin acme.com alice alicia
#
# This function add new email alias.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
account=$3
malias=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '4' "$#" 'USER DOMAIN ACCOUNT ALIAS'
is_format_valid 'user' 'domain' 'account' 'malias'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_valid "mail/$domain" 'ACCOUNT' "$account"
is_object_unsuspended "mail/$domain" 'ACCOUNT' "$account"
is_mail_new "$malias"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding exim alias
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
str="$malias@$domain_idn:$account@$domain_idn"
echo "$str" >> $HOMEDIR/$user/conf/mail/$domain/aliases
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding hestia alias
aliases=$(get_object_value "mail/$domain" 'ACCOUNT' "$account" '$ALIAS')
if [ -z "$aliases" ]; then
aliases="$malias"
else
aliases="$aliases,$malias"
fi
update_object_value "mail/$domain" 'ACCOUNT' "$account" '$ALIAS' "$aliases"
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "Added alias $malias to mail account $account@$domain."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,85 @@
#!/bin/bash
# info: add mail account autoreply message
# options: USER DOMAIN ACCOUNT MESSAGE
#
# example: v-add-mail-account-autoreply admin example.com user Hello from e-mail!
#
# This function add new email account.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
account=$3
autoreply=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Define mail user
if [ "$MAIL_SYSTEM" = 'exim4' ]; then
MAIL_USER=Debian-exim
else
MAIL_USER=exim
fi
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '4' "$#" 'USER DOMAIN ACCOUNT MESSAGE'
is_format_valid 'user' 'domain' 'account' 'autoreply'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_valid "mail/$domain" 'ACCOUNT' "$account"
is_object_unsuspended "mail/$domain" 'ACCOUNT' "$account"
# is_object_value_empty "mail/$domain" 'ACCOUNT' "$account" '$AUTOREPLY'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding exim autoreply
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
msg="$HOMEDIR/$user/conf/mail/$domain/autoreply.$account.msg"
echo -e "$autoreply" > $msg
chown $MAIL_USER:mail $msg
chmod 660 $msg
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding autoreply message
echo -e "$autoreply" > $USER_DATA/mail/$account@$domain.msg
chmod 660 $USER_DATA/mail/$account@$domain.msg
update_object_value "mail/$domain" 'ACCOUNT' "$account" '$AUTOREPLY' 'yes'
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "Added auto-reply message for mail account $account@$domain."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,88 @@
#!/bin/bash
# info: add mail account forward address
# options: USER DOMAIN ACCOUNT FORWARD
#
# example: v-add-mail-account-forward admin acme.com alice bob
#
# This function add new email account.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
account=$3
email_forward=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '4' "$#" 'USER DOMAIN ACCOUNT FORWARD'
is_format_valid 'user' 'domain' 'account'
if [ "$email_forward" != ':blackhole:' ]; then
is_format_valid 'email_forward'
fi
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_valid "mail/$domain" 'ACCOUNT' "$account"
is_object_unsuspended "mail/$domain" 'ACCOUNT' "$account"
fwd=$(get_object_value "mail/$domain" 'ACCOUNT' "$account" '$FWD')
if [ -n "$(echo $fwd | grep -w "$email_forward")" ]; then
echo "Error: forward $email_forward exists"
log_event "$E_EXISTS" "$ARGUMENTS"
exit $E_EXISTS
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Define fwd string
if [ -z "$fwd" ]; then
fwd="$email_forward"
else
fwd="$fwd,$email_forward"
fi
# Adding forward to exim
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
sed -i "/^$account@$domain_idn:/ d" $HOMEDIR/$user/conf/mail/$domain/aliases
echo "$account@$domain_idn:$fwd" >> $HOMEDIR/$user/conf/mail/$domain/aliases
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Updating config
update_object_value "mail/$domain" 'ACCOUNT' "$account" '$FWD' "$fwd"
# Logging
$BIN/v-log-action "$user" "Warning" "Mail" "Mail forwarding on mail account $account@$domain enabled (send to: $email_forward)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,86 @@
#!/bin/bash
# info: add mail account forward-only flag
# options: USER DOMAIN ACCOUNT
#
# example: v-add-mail-account-fwd-only admin example.com user
#
# This function adds fwd-only flag
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
account=$3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Define mail user
if [ "$MAIL_SYSTEM" = 'exim4' ]; then
MAIL_USER=Debian-exim
else
MAIL_USER=exim
fi
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN ACCOUNT'
is_format_valid 'user' 'domain' 'account'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_valid "mail/$domain" 'ACCOUNT' "$account"
is_object_unsuspended "mail/$domain" 'ACCOUNT' "$account"
fwd=$(get_object_value "mail/$domain" 'ACCOUNT' "$account" '$FWD')
if [ -z "$fwd" ]; then
echo "Error: forward doesn't exist"
log_event "$E_NOTEXIST" "$ARGUMENTS"
exit "$E_NOTEXIST"
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding account to fwd_only
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
echo "$account" >> $HOMEDIR/$user/conf/mail/$domain/fwd_only
chown -R $MAIL_USER:mail $HOMEDIR/$user/conf/mail/$domain/fwd_only
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Updating config
add_object_key "mail/$domain" 'ACCOUNT' "$account" 'FWD_ONLY' 'MD5'
update_object_value "mail/$domain" 'ACCOUNT' "$account" '$FWD_ONLY' "yes"
# Logging
$BIN/v-log-action "$user" "Warning" "Mail" "Forward-only flag enabled on mail account $account@$domain."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,224 @@
#!/bin/bash
# info: add mail domain
# options: USER DOMAIN [ANTISPAM] [ANTIVIRUS] [DKIM] [DKIM_SIZE] [RESTART] [REJECT_SPAM]
#
# example: v-add-mail-domain admin mydomain.tld
#
# This function adds MAIL domain.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
antispam=${3-yes}
antivirus=${4-yes}
dkim=${5-yes}
dkim_size=${6-1024}
restart="$7"
reject=${8-no}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# shellcheck source=/usr/local/hestia/func/syshealth.sh
source $HESTIA/func/syshealth.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Define mail user
if [ "$MAIL_SYSTEM" = 'exim4' ]; then
MAIL_USER=Debian-exim
else
MAIL_USER=exim
fi
# Additional argument formatting
format_domain
format_domain_idn
domain_utf=$(idn2 --quiet -d "$domain_idn")
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN [ANTISPAM] [ANTIVIRUS] [DKIM] [DKIM_SIZE] [RESTART] [REJECT_SPAM]'
is_format_valid 'user' 'domain' 'antispam' 'antivirus' 'dkim' 'dkim_size' 'restart' 'reject'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
if [ "$($BIN/v-list-mail-domain $user $domain_utf plain | cut -f 1) " != "$domain" ]; then
is_domain_new 'mail' "$domain_utf"
fi
if [ "$($BIN/v-list-mail-domain $user $domain_idn plain | cut -f 1) " != "$domain" ]; then
is_domain_new 'mail' "$domain_idn"
else
is_domain_new 'mail' "$domain"
fi
if [ -z "$(is_ip_format_valid $domain)" ]; then
echo "Error: Invalid domain format. IP address detected as input."
exit 1
fi
is_package_full 'MAIL_DOMAINS'
is_dir_symlink $HOMEDIR/$user/mail
is_base_domain_owner "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
source_conf "$USER_DATA/user.conf"
# Inherit web domain local ip address
domain_ip=$(get_object_value 'web' 'DOMAIN' "$domain" '$IP')
if [ ! -z "$domain_ip" ]; then
local_ip=$(get_real_ip "$domain_ip")
is_ip_valid "$local_ip" "$user"
else
get_user_ip
fi
# Generating timestamp
new_timestamp
if [ -z "$ANTISPAM_SYSTEM" ]; then
antispam="no"
reject="no"
fi
if [ -z "$ANTIVIRUS_SYSTEM" ]; then
antivirus="no"
fi
# Adding domain to mail.conf
s="DOMAIN='$domain' ANTIVIRUS='$antivirus' ANTISPAM='$antispam' REJECT='$reject' DKIM='$dkim' WEBMAIL=''"
s="$s SSL='no' LETSENCRYPT='no' CATCHALL='' ACCOUNTS='0' RATE_LIMIT='$RATE_LIMIT' U_DISK='0' SUSPENDED='no' TIME='$time'"
s="$s DATE='$date'"
echo $s >> $USER_DATA/mail.conf
touch $USER_DATA/mail/$domain.conf
syshealth_repair_mail_config
# Generating DKIM keys
if [ "$dkim" = 'yes' ]; then
openssl genrsa -out $USER_DATA/mail/$domain.pem $dkim_size &> /dev/null
openssl rsa -pubout -in $USER_DATA/mail/$domain.pem \
-out $USER_DATA/mail/$domain.pub &> /dev/null
fi
# Set permissions
chmod 660 $USER_DATA/mail/$domain.*
chmod 660 $USER_DATA/mail.conf
# Building exim configs
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
mkdir $HOMEDIR/$user/conf/mail/$domain
mkdir $HOMEDIR/$user/mail/$domain_idn
touch $HOMEDIR/$user/conf/mail/$domain/aliases
touch $HOMEDIR/$user/conf/mail/$domain/passwd
touch $HOMEDIR/$user/conf/mail/$domain/fwd_only
touch $HOMEDIR/$user/conf/mail/$domain/accounts
ln -s $HOMEDIR/$user/conf/mail/$domain \
/etc/$MAIL_SYSTEM/domains/$domain_idn
# Seeting outgoing ip address
if [ -n "$local_ip" ]; then
echo "$local_ip" > $HOMEDIR/$user/conf/mail/$domain/ip
fi
if [ -n "$ANTISPAM_SYSTEM" ]; then
# Adding antispam protection
if [ "$antispam" = 'yes' ]; then
touch "$HOMEDIR/$user/conf/mail/$domain/antispam"
fi
if [ "$reject" = 'yes' ]; then
touch "$HOMEDIR/$user/conf/mail/$domain/reject_spam"
fi
fi
if [ -n "$ANTIVIRUS_SYSTEM" ]; then
# Adding antivirus protection
if [ "$antivirus" = 'yes' ]; then
touch "$HOMEDIR/$user/conf/mail/$domain/antivirus"
fi
fi
# Adding dkim support
if [ "$dkim" = 'yes' ]; then
cp -f $USER_DATA/mail/$domain.pem \
$HOMEDIR/$user/conf/mail/$domain/dkim.pem
fi
# Set permission
chmod 771 $HOMEDIR/$user/conf/mail/$domain
chmod 660 $HOMEDIR/$user/conf/mail/$domain/*
chmod 771 /etc/$MAIL_SYSTEM/domains/$domain_idn
chmod 770 $HOMEDIR/$user/mail/$domain_idn
# Set ownership
chown -R $MAIL_USER:mail $HOMEDIR/$user/conf/mail/$domain
if [ "$IMAP_SYSTEM" = 'dovecot' ]; then
chown -R dovecot:mail $HOMEDIR/$user/conf/mail/$domain/passwd
fi
chown $MAIL_USER:mail $HOMEDIR/$user/conf/mail/$domain/accounts
chown $user:mail $HOMEDIR/$user/mail/$domain_idn
fi
# Adding dkim dns records
if [ -n "$DNS_SYSTEM" ] && [ "$dkim" = 'yes' ]; then
check_dns_domain=$(is_object_valid 'dns' 'DOMAIN' "$domain")
if [ "$?" -eq 0 ]; then
p=$(cat $USER_DATA/mail/$domain.pub | grep -v ' KEY---' | tr -d '\n')
record='_domainkey'
policy="\"t=y; o=~;\""
$BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$policy" '' '' 'no' '' 'yes'
record='mail._domainkey'
selector="\"v=DKIM1\; k=rsa\; p=$p\""
$BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$selector" '' '' 'yes' '' 'yes'
fi
fi
# Add webmail configuration to mail domain
if [ -n "$WEB_SYSTEM" ] || [ -n "$PROXY_SYSTEM" ]; then
if [ -n "$IMAP_SYSTEM" ]; then
$BIN/v-add-mail-domain-webmail "$user" "$domain" '' 'no'
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Increasing domain value
increase_user_value "$user" '$U_MAIL_DOMAINS'
if [ "$dkim" = 'yes' ]; then
increase_user_value "$user" '$U_MAIL_DKIM'
fi
# Restarting web server
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
# Restarting proxy server
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "Added new mail domain ($domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,69 @@
#!/bin/bash
# info: add mail domain antispam support
# options: USER DOMAIN
#
# example: v-add-mail-domain-antispam admin mydomain.tld
#
# This function enables spamassasin for incoming emails.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN'
is_format_valid 'user' 'domain'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_value_empty 'mail' 'DOMAIN' "$domain" '$ANTISPAM'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding antispam flag
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
touch $HOMEDIR/$user/conf/mail/$domain/antispam
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding antispam in config
update_object_value 'mail' 'DOMAIN' "$domain" '$ANTISPAM' 'yes'
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "Anti-spam protection enabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,69 @@
#!/bin/bash
# info: add mail domain antivirus support
# options: USER DOMAIN
#
# example: v-add-mail-domain-antivirus admin mydomain.tld
#
# This function enables clamav scan for incoming emails.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN'
is_format_valid 'user' 'domain'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_value_empty 'mail' 'DOMAIN' "$domain" '$ANTIVIRUS'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding antivirus flag
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
touch $HOMEDIR/$user/conf/mail/$domain/antivirus
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding antivirus in config
update_object_value 'mail' 'DOMAIN' "$domain" '$ANTIVIRUS' 'yes'
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "Anti-virus scanning enabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,70 @@
#!/bin/bash
# info: add mail domain catchall account
# options: USER DOMAIN EMAIL
#
# example: v-add-mail-domain-catchall admin example.com master@example.com
#
# This function enables catchall account for incoming emails.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
email="$3"
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN EMAIL'
is_format_valid 'user' 'domain' 'email'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_value_empty 'mail' 'DOMAIN' "$domain" '$CATCHALL'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding catchall alias
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
sed -i "/*@$domain_idn:/d" $HOMEDIR/$user/conf/mail/$domain/aliases
echo "*@$domain_idn:$email" >> $HOMEDIR/$user/conf/mail/$domain/aliases
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding antispam in config
update_object_value 'mail' 'DOMAIN' "$domain" '$CATCHALL' "$email"
# Logging
$BIN/v-log-action "$user" "Warning" "Mail" "Catch-all forwarding enabled (Domain: $domain, Send to: $email)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,98 @@
#!/bin/bash
# info: add mail domain dkim support
# options: USER DOMAIN [DKIM_SIZE]
#
# example: v-add-mail-domain-dkim admin acme.com
#
# This function adds DKIM signature to outgoing domain emails.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
dkim_size=${3-2048}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Define mail user
if [ "$MAIL_SYSTEM" = 'exim4' ]; then
MAIL_USER=Debian-exim
else
MAIL_USER=exim
fi
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN [DKIM_SIZE]'
is_format_valid 'user' 'domain' 'dkim_size'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_value_empty 'mail' 'DOMAIN' "$domain" '$DKIM'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating dkim
openssl genrsa -out $USER_DATA/mail/$domain.pem $dkim_size &> /dev/null
openssl rsa -pubout -in $USER_DATA/mail/$domain.pem \
-out $USER_DATA/mail/$domain.pub &> /dev/null
chmod 660 $USER_DATA/mail/$domain.*
# Adding dkim keys
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
cp $USER_DATA/mail/$domain.pem $HOMEDIR/$user/conf/mail/$domain/dkim.pem
chown $MAIL_USER:mail $HOMEDIR/$user/conf/mail/$domain/dkim.pem
chmod 660 $HOMEDIR/$user/conf/mail/$domain/dkim.pem
fi
# Adding dns records
if [ -n "$DNS_SYSTEM" ] && [ -e "$USER_DATA/dns/$domain.conf" ]; then
p=$(cat $USER_DATA/mail/$domain.pub | grep -v ' KEY---' | tr -d '\n')
record="_domainkey"
policy="\"t=y\; o=~\;\""
$BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$policy" '' '' 'no' '' 'yes'
record="mail._domainkey"
selector="\"v=DKIM1\; k=rsa\; p=$p\""
$BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$selector" '' '' 'yes' '' 'yes'
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding dkim in config
update_object_value 'mail' 'DOMAIN' "$domain" '$DKIM' 'yes'
increase_user_value "$user" '$U_MAIL_DKIM'
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "DKIM message signing enabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,71 @@
#!/bin/bash
# info: add mail domain reject spam support
# options: USER DOMAIN
# labels: mail
#
# example: v-add-mail-domain-reject admin mydomain.tld
#
# The function enables spam rejection for incoming emails.
#----------------------------------------------------------#
# Variable&Function #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN'
is_format_valid 'user' 'domain'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_value_empty 'mail' 'DOMAIN' "$domain" '$REJECT'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding antivirus flag
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
# Reject spam > 10 when enabled
touch $HOMEDIR/$user/conf/mail/$domain/reject_spam
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding antivirus in config
update_object_value 'mail' 'DOMAIN' "$domain" '$REJECT' 'yes'
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "Anti-virus scanning enabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,81 @@
#!/bin/bash
# info: Add mail domain smtp relay support
# options: USER DOMAIN HOST [USERNAME] [PASSWORD] [PORT]
#
# example: v-add-mail-domain-smtp-relay user domain.tld srv.smtprelay.tld uname123 pass12345
#
# This function adds mail domain smtp relay support.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
host=$3
username=$4
password=$5
port=${6-587}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN HOST [USERNAME] [PASSWORD] [PORT]'
is_format_valid 'port' 'user' 'domain' 'host'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_password_valid "$password" "Password"
is_username_format_valid "$username" "Username"
format_no_quotes "$password" "Password"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
cat > /etc/exim4/domains/${domain}/smtp_relay.conf << EOL
host:$host
port:$port
user:$username
pass:$password
EOL
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding smtp relay values in config
if [ -z "$U_SMTP_RELAY" ]; then
add_object_key 'mail' 'DOMAIN' "$domain" 'U_SMTP_RELAY' 'ACCOUNTS'
add_object_key 'mail' 'DOMAIN' "$domain" 'U_SMTP_RELAY_HOST' 'ACCOUNTS'
add_object_key 'mail' 'DOMAIN' "$domain" 'U_SMTP_RELAY_PORT' 'ACCOUNTS'
add_object_key 'mail' 'DOMAIN' "$domain" 'U_SMTP_RELAY_USERNAME' 'ACCOUNTS'
add_object_key 'mail' 'DOMAIN' "$domain" 'U_SMTP_RELAY_PASSWORD' 'ACCOUNTS'
fi
update_object_value 'mail' 'DOMAIN' "$domain" '$U_SMTP_RELAY' 'true'
update_object_value 'mail' 'DOMAIN' "$domain" '$U_SMTP_RELAY_HOST' "$host"
update_object_value 'mail' 'DOMAIN' "$domain" '$U_SMTP_RELAY_PORT' "$port"
update_object_value 'mail' 'DOMAIN' "$domain" '$U_SMTP_RELAY_USERNAME' "$username"
update_object_value 'mail' 'DOMAIN' "$domain" '$U_SMTP_RELAY_PASSWORD' "$password"
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "SMTP Relay enabled (Domain: $domain, Host: $host, Port: $port)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,144 @@
#!/bin/bash
# info: add mail SSL for $domain
# options: USER DOMAIN SSL_DIR [RESTART]
#
# This function turns on SSL support for a mail domain. Parameter ssl_dir
# is a path to a directory where 2 or 3 ssl files can be found. Certificate file
# mail.domain.tld.crt and its key mail.domain.tld.key are mandatory. Certificate
# authority mail.domain.tld.ca file is optional.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
ssl_dir=$3
restart="$4"
# Additional argument formatting
if [[ "$domain" =~ [[:upper:]] ]]; then
domain=$(echo "$domain" | tr '[:upper:]' '[:lower:]')
fi
if [[ "$domain" =~ ^www\..* ]]; then
domain=$(echo "$domain" | sed -e "s/^www.//")
fi
if [[ "$domain" =~ .*\.$ ]]; then
domain=$(echo "$domain" | sed -e "s/\.$//")
fi
domain_idn=$(idn2 --quiet "$domain")
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN SSL_DIR [RESTART]'
is_format_valid 'user' 'domain' 'ssl_dir'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
is_object_value_empty 'mail' 'DOMAIN' "$domain" '$SSL'
is_web_domain_cert_valid
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
if [ -n "$restart" ]; then
is_format_valid "$restart"
fi
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Inherit web domain local ip address
domain_ip=$(get_object_value 'web' 'DOMAIN' "$domain" '$IP')
if [ -n "$domain_ip" ]; then
local_ip=$(get_real_ip "$domain_ip")
is_ip_valid "$local_ip" "$user"
else
get_user_ip
fi
# Call routine to add SSL configuration to mail domain
add_mail_ssl_config
if [ "$WEBMAIL" == "roundcube" ]; then
WEBMAIL_TEMPLATE="default"
if [ -n "$PROXY_SYSTEM" ]; then
PROXY_TEMPLATE="default"
fi
# Add webmail configuration to mail domain
WEBMAIL_TEMPLATE="default"
if [ "$WEB_SYSTEM" = "nginx" ]; then
WEBMAIL_TEMPLATE="web_system"
fi
elif [ "$WEBMAIL" == "snappymail" ]; then
WEBMAIL_TEMPLATE="snappymail"
if [ -n "$PROXY_SYSTEM" ]; then
PROXY_TEMPLATE="default_snappymail"
fi
elif [ "$WEBMAIL" == "rainloop" ]; then
WEBMAIL_TEMPLATE="rainloop"
if [ -n "$PROXY_SYSTEM" ]; then
PROXY_TEMPLATE="default_rainloop"
fi
else
WEBMAIL_TEMPLATE="disabled"
if [ -n "$PROXY_SYSTEM" ]; then
PROXY_TEMPLATE="default_disabled"
fi
fi
add_webmail_config "$WEB_SYSTEM" "${WEBMAIL_TEMPLATE}.stpl"
if [ -n "$PROXY_SYSTEM" ]; then
add_webmail_config "$PROXY_SYSTEM" "${PROXY_TEMPLATE}.stpl"
fi
# Increase value for domain
increase_user_value "$user" '$U_MAIL_SSL'
# Set SSL as enabled in configuration
update_object_value 'mail' 'DOMAIN' "$domain" '$SSL' "yes"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restarting mail server
$BIN/v-restart-mail "$restart"
check_result $? "Mail restart failed" > /dev/null
# Restarting web server
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
# Restarting proxy server
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "Mail" "SSL enabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,190 @@
#!/bin/bash
# info: add webmail support for a domain
# options: USER DOMAIN [WEBMAIL] [RESTART] [QUIET]
#
# example: v-add-sys-webmail user domain.com
# example: v-add-sys-webmail user domain.com snappymail
# example: v-add-sys-webmail user domain.com roundcube
#
# This function enables webmail client for a mail domain.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
webmail=$3
restart="$4"
quiet=$5
if [ -z "$restart" ]; then
restart="yes"
fi
# Additional argument formatting
if [[ "$domain" =~ [[:upper:]] ]]; then
domain=$(echo "$domain" | tr '[:upper:]' '[:lower:]')
fi
if [[ "$domain" =~ ^www\..* ]]; then
domain=$(echo "$domain" | sed -e "s/^www.//")
fi
if [[ "$domain" =~ .*\.$ ]]; then
domain=$(echo "$domain" | sed -e "s/\.$//")
fi
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
if [ -z "$webmail" ]; then
for client in ${WEBMAIL_SYSTEM//,/ }; do
webmail="$client"
done
fi
check_args '2' "$#" 'USER DOMAIN [WEBMAIL] [RESTART]'
is_format_valid 'user' 'domain' 'restart'
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
is_system_enabled "$IMAP_SYSTEM" 'IMAP_SYSTEM'
is_type_valid "$WEBMAIL_SYSTEM disabled" "$webmail"
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'mail' 'DOMAIN' "$domain"
is_object_unsuspended 'mail' 'DOMAIN' "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Inherit web domain local ip address
domain_ip=$(get_object_value 'web' 'DOMAIN' "$domain" '$IP')
if [ -n "$domain_ip" ]; then
local_ip=$(get_real_ip "$domain_ip")
is_ip_valid "$local_ip" "$user"
ip=$local_ip
nat_ip=$(get_ip_value '$NAT')
if [ -n "$nat_ip" ]; then
ip=$nat_ip
fi
else
get_user_ip
fi
# Verify that webmail alias variable exists and create it if it does not
if [ -z "$WEBMAIL_ALIAS" ]; then
$BIN/v-change-sys-config-value 'WEBMAIL_ALIAS' "webmail"
else
# Ensure DNS record exists if Hestia is hosting DNS zones
if [ -n "$DNS_SYSTEM" ]; then
dns_domain=$($BIN/v-list-dns-domains $user | grep $domain | cut -d' ' -f1)
webmail_record=$($BIN/v-list-dns-records $user $domain | grep -i " $WEBMAIL_ALIAS " | cut -d' ' -f1)
if [ "$dns_domain" = "$domain" ]; then
if [ "$WEBMAIL_ALIAS" != "mail" ]; then
#Prevent mail.domain.com to be cycled
if [ -z "$webmail_record" ]; then
if [ "$quiet" = "yes" ]; then
$BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
else
$BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
fi
else
if [ "$quiet" = "yes" ]; then
$BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes'
$BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
else
$BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes'
$BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes'
fi
fi
fi
fi
fi
if [ "$webmail" == "roundcube" ]; then
WEBMAIL_TEMPLATE="default"
if [ -n "$PROXY_SYSTEM" ]; then
PROXY_TEMPLATE="default"
fi
# Add webmail configuration to mail domain
WEBMAIL_TEMPLATE="default"
if [ "$WEB_SYSTEM" = "nginx" ]; then
WEBMAIL_TEMPLATE="web_system"
fi
elif [ -f "$HESTIA/data/templates/mail/$WEB_SYSTEM/$webmail.tpl" ]; then
WEBMAIL_TEMPLATE="$webmail"
if [ -n "$PROXY_SYSTEM" ]; then
PROXY_TEMPLATE="default_$webmail"
fi
else
WEBMAIL_TEMPLATE="disabled"
if [ -n "$PROXY_SYSTEM" ]; then
PROXY_TEMPLATE="default_disabled"
fi
fi
add_webmail_config "$WEB_SYSTEM" "${WEBMAIL_TEMPLATE}.tpl"
if [ -n "$PROXY_SYSTEM" ]; then
add_webmail_config "$PROXY_SYSTEM" "${PROXY_TEMPLATE}.tpl"
fi
# Enable SSL for webmail if available
if [ -f $HOMEDIR/$user/conf/mail/$domain/ssl/$domain.crt ] || [ "$SSL" = 'yes' ]; then
add_webmail_config "$WEB_SYSTEM" "${WEBMAIL_TEMPLATE}.stpl"
if [ -n "$PROXY_SYSTEM" ]; then
add_webmail_config "$PROXY_SYSTEM" "${PROXY_TEMPLATE}.stpl"
fi
fi
fi
WEBMAIL=$(get_object_value 'web' 'DOMAIN' "$domain" "$WEBMAIL")
if [ -z "$WEBMAIL" ]; then
add_object_key 'mail' 'DOMAIN' "$domain" 'WEBMAIL' 'SSL'
fi
# Set SSL as enabled in configuration
update_object_value 'mail' 'DOMAIN' "$domain" '$WEBMAIL' "$webmail"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
if [ "$restart" = 'yes' ]; then
# Restarting web server
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
fi
# Logging
if [ "$quiet" != 'yes' ]; then
$BIN/v-log-action "$user" "Info" "Mail" "Webmail access enabled (Domain: $domain)."
fi
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,124 @@
#!/bin/bash
# info: add remote dns domain
# options: USER DOMAIN [FLUSH]
#
# example: v-add-remote-dns-domain admin mydomain.tld yes
#
# This function synchronise dns domain with the remote server.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
flush=$3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/remote.sh
source $HESTIA/func/remote.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN [FLUSH]'
is_format_valid 'user' 'domain'
if [ -n "$flush" ]; then
is_type_valid "records yes no" "$flush"
fi
is_system_enabled "$DNS_CLUSTER" 'DNS_CLUSTER'
is_procces_running
remote_dns_health_check
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Parsing domain record
str=$(grep "DOMAIN='$domain'" $USER_DATA/dns.conf 2> /dev/null)
if [ -z "$str" ]; then
pipe="$HESTIA/data/queue/dns-cluster.pipe"
queue_str=$(grep -n "$SCRIPT $1 $2 " $pipe | cut -f1 -d: | head -n1)
if [ -n "$queue_str" ]; then
sed -i "$queue_str d" $pipe
fi
exit
fi
if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
str=$(echo "$str" | sed "s/SLAVE='no'/SLAVE='yes'/g")
str=$(echo "$str" | sed "s/SLAVE=''/SLAVE='yes'/g")
ip=$(ip addr | grep 'inet ' | grep global | head -n1 | awk '{print $2}' | cut -f1 -d/)
source_conf $HESTIA/data/ips/$ip
if [ -z $NAT ]; then
str=$(echo "$str" | sed "s/MASTER=''/MASTER='$ip'/g")
else
str=$(echo "$str" | sed "s/MASTER=''/MASTER='$NAT'/g")
fi
fi
IFS=$'\n'
for cluster in $(grep "SUSPENDED='no'" $HESTIA/conf/dns-cluster.conf); do
# Reset user, password and hash vars
clear_dns_cluster_settings
# Parsing remote dns host parameters
parse_object_kv_list "$cluster"
# Parsing domain parameters
parse_object_kv_list "$str"
if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
# Syncing domain data
cluster_cmd v-insert-dns-domain $DNS_USER "$str" $HOSTNAME $flush 'no'
check_result $? "$HOST connection failed" "$E_CONNECT"
cluster_cmd v-rebuild-dns-domain $DNS_USER $domain 'yes' 'no'
check_result $? "$HOST connection failed" "$E_CONNECT"
else
# Syncing domain data
cluster_cmd v-insert-dns-domain $DNS_USER "$str" $HOSTNAME $flush 'no'
check_result $? "$HOST connection failed" "$E_CONNECT"
# Syncing domain records
tmp_file="/tmp/vst-sync.$DOMAIN"
cluster_file $USER_DATA/dns/$DOMAIN.conf $tmp_file
check_result $? "$HOST connection failed" "$E_CONNECT"
# Inserting synced records
cluster_cmd v-insert-dns-records $DNS_USER $DOMAIN $tmp_file 'no'
check_result $? "$HOST connection failed" "$E_CONNECT"
# Rebuilding dns zone
cluster_cmd v-rebuild-dns-domain $DNS_USER $domain 'yes' 'no'
check_result $? "$HOST connection failed" "$E_CONNECT"
fi
done
if [ "$DNS_CLUSTER_SYSTEM" = "hestia-zone" ]; then
rndc notify $domain > /dev/null 2>&1
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Updating pipe
rm -f $tmpfile
pipe="$HESTIA/data/queue/dns-cluster.pipe"
str=$(grep -n "$SCRIPT $1 $2 " $pipe | cut -f1 -d: | head -n1)
if [ -n "$str" ]; then
sed -i "$str d" $pipe
fi
exit

@ -0,0 +1,131 @@
#!/bin/bash
# info: add new remote dns host
# options: HOST PORT USER PASSWORD [TYPE] [DNS_USER]
#
# example: v-add-remote-dns-host slave.your_host.com 8083 admin your_passw0rd
#
# example: v-add-remote-dns-host slave.your_host.com 8083 api_key ''
#
# This function adds remote dns server to the dns cluster.
# As alternative api_key generated on the slave server.
# See v-generate-api-key can be used to connect the remote dns server
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
host=$1
HOST=$host
port=$2
PORT=$port
user=$3
USER=$user
hash=$user
HASH=$user
password=$4
HIDE=4
PASSWORD=$password
type=${5}
TYPE="$type"
dns_user=${6-dns-cluster}
DNS_USER=$dns_user
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/remote.sh
source $HESTIA/func/remote.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
if [ -z "$type" ]; then
type='api'
TYPE="$type"
fi
args_usage='HOST PORT USER [PASSWORD] [TYPE] [DNS_USER]'
check_args '3' "$#" "$args_usage"
is_format_valid 'host' 'port' 'dns_user'
if [ -z "$password" ]; then
is_format_valid 'hash'
else
is_format_valid 'user'
fi
is_type_valid "api ssh" "$type"
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_password_valid
is_dnshost_new
is_dnshost_alive
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
if [ -z "$password" ]; then
# Concatentating dns host string
str="HOST='$host' PORT='$port' HASH='$hash'"
str="$str DNS_USER='$dns_user' TYPE='$type' SUSPENDED='no'"
str="$str TIME='$time' DATE='$date'"
else
# Concatentating dns host string
str="HOST='$host' PORT='$port' USER='$user' PASSWORD='$password'"
str="$str DNS_USER='$dns_user' TYPE='$type' SUSPENDED='no'"
str="$str TIME='$time' DATE='$date'"
fi
# Adding host to dns-cluster.conf
echo "$str" >> $HESTIA/conf/dns-cluster.conf
chmod 660 $HESTIA/conf/dns-cluster.conf
# Enabling DNS_CLUSTER
if [ -z "$(grep DNS_CLUSTER= $HESTIA/conf/hestia.conf)" ]; then
sed -i "s/^STATS_/DNS_CLUSTER='yes'\nSTATS_/g" $HESTIA/conf/hestia.conf
else
sed -i "s/DNS_CLUSTER=.*/DNS_CLUSTER='yes'/g" $HESTIA/conf/hestia.conf
fi
# Enabling remote dns-cluster queue
cluster_cmd v-add-cron-restart-job
check_result $? "$HOST connection failed" "$E_CONNECT"
# Loop trough domains to generate new serial
for dns_user in $($BIN/v-list-sys-users plain); do
for dns_domain in $($BIN/v-list-dns-domains $dns_user plain | cut -f1); do
$BIN/v-rebuild-dns-domain $dns_user $dns_domain "no" "yes"
done
done
# Syncing all domains
$BIN/v-sync-dns-cluster
check_result $? "$HOST sync failed" "$E_CONNECT"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding local dns-cluster cron job
cmd="sudo $BIN/v-update-sys-queue dns-cluster"
check_cron=$(grep "$cmd" $HESTIA/data/users/admin/cron.conf 2> /dev/null)
if [ -z "$check_cron" ] && [ ! -z "$CRON_SYSTEM" ]; then
$BIN/v-add-cron-job admin '*/5' '*' '*' '*' '*' "$cmd"
fi
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,98 @@
#!/bin/bash
# info: add remote dns domain record
# options: USER DOMAIN ID
#
# example: v-add-remote-dns-record bob acme.com 23
#
# This function synchronise dns domain with the remote server.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
id=$3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/remote.sh
source $HESTIA/func/remote.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN ID'
is_format_valid 'user' 'domain' 'id'
is_system_enabled "$DNS_CLUSTER" 'DNS_CLUSTER'
is_object_valid 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_procces_running
remote_dns_health_check
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Parsing record
str=$(grep "ID='$id'" $USER_DATA/dns/$domain.conf)
if [ -z "$str" ]; then
pipe="$HESTIA/data/queue/dns-cluster.pipe"
queue_str=$(grep -n "$SCRIPT $1 $2 $3$" $pipe | cut -f1 -d: | head -n1)
if [ -n "$queue_str" ]; then
sed -i "$queue_str d" $pipe
fi
exit
fi
# $DNS_CLUSTER_SYSTEM = "hestia-zone" doesn't need to be uopdated
if [ "$DNS_CLUSTER_SYSTEM" != "hestia-zone" ]; then
IFS=$'\n'
for cluster in $(grep "SUSPENDED='no'" $HESTIA/conf/dns-cluster.conf); do
# Reset user, password and hash vars
clear_dns_cluster_settings
# Parsing remote host parameters
parse_object_kv_list "$cluster"
# Syncing serial
str=$(grep "DOMAIN='$domain'" $USER_DATA/dns.conf)
# Parsing domain parameters
parse_object_kv_list "$str"
cluster_cmd v-insert-dns-domain $DNS_USER "$str" $HOSTNAME 'domain' 'no'
check_result $? "$HOST connection failed (soa sync)" "$E_CONNECT"
# Syncing record
str=$(grep "ID='$id'" $USER_DATA/dns/$domain.conf)
cluster_cmd v-insert-dns-record $DNS_USER $domain "$str" 'no'
check_result $? "$HOST connection failed (record sync)" "$E_CONNECT"
# Rebuilding dns zone
cluster_cmd v-rebuild-dns-domain $DNS_USER $domain 'yes' 'no'
check_result $? "$HOST connection failed (rebuild)" "$E_CONNECT"
done
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Updating pipe
pipe="$HESTIA/data/queue/dns-cluster.pipe"
str=$(grep -n "$SCRIPT $1 $2 $3$" $pipe | cut -f1 -d: | head -n1)
if [ -n "$str" ]; then
sed -i "$str d" $pipe
fi
exit

@ -0,0 +1,49 @@
#!/bin/bash
# info: add IP address to API allow list
# options: IP
#
# example: v-add-sys-api-ip 1.1.1.1
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
ip46=${1// /}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'IP'
is_format_valid 'ip46'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
if [ "$API_ALLOWED_IP" != "" ]; then
$BIN/v-change-sys-config-value 'API_ALLOWED_IP' "$API_ALLOWED_IP,$ip46"
else
$BIN/v-change-sys-config-value 'API_ALLOWED_IP' "$ip46"
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Warning" "System" "Added new IP address added to Allowed IP API (IP: $ip46)"
log_event "$OK" "$ARGUMENTS"

@ -0,0 +1,109 @@
#!/bin/bash
# Add php dependencies to Hestia
# options: [MODE]
#
# This function install PHPMailer and quoteshellarg as via composer
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# upgrade config file
source "$HESTIA/install/upgrade/upgrade.conf"
MODE=$1
user="admin"
PM_INSTALL_DIR="$HESTIA/web/inc"
QUICK_INSTALL_DIR="$HESTIA/web/src"
COMPOSER_BIN="$HOMEDIR/$user/.composer/composer"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
# Checking root permissions
if [ "x$(id -u)" != 'x0' ]; then
echo "ERROR: v-add-sys-dependencies can be run executed only by root user"
exit 10
fi
# Ensure that $HESTIA (/usr/local/hestia/) and other variables are valid.
if [ -z "$HESTIA" ]; then
HESTIA="/usr/local/hestia"
fi
if [ -z "$HOMEDIR" ] || [ -z "$HESTIA_INSTALL_DIR" ]; then
echo "ERROR: Environment variables not present, installation aborted."
exit 2
fi
# Ensure that Composer is installed for the user before continuing as it is a dependency of the PHPMailer.
if [ ! -f "$COMPOSER_BIN" ]; then
$BIN/v-add-user-composer "$user"
if [ $? -ne 0 ]; then
$BIN/v-add-user-notification admin 'Composer installation failed!' '<p class="u-text-bold">Hestia will not work without Composer.</p><p>Please try running the installer manually from a shell session:<br><code>v-add-sys-dependencies</code></p><p>If this continues, <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.</p>'
exit 1
fi
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
cd "$PM_INSTALL_DIR"
rm --recursive --force ${PM_INSTALL_DIR}/vendor
mkdir -p ${PM_INSTALL_DIR}/vendor
chown $user: -R ${PM_INSTALL_DIR}/vendor
openssl_installed=$(/usr/local/hestia/php/bin/php -m | grep openssl)
if [ -z "$openssl_installed" ]; then
COMPOSER_HOME="$HOMEDIR/$user/.config/composer" user_exec /usr/bin/php $COMPOSER_BIN --quiet --no-dev install
else
COMPOSER_HOME="$HOMEDIR/$user/.config/composer" user_exec $HESTIA_PHP $COMPOSER_BIN --quiet --no-dev install
fi
# Check if installation was successful, if not abort script and throw error message notification and clean-up
if [ $? -ne 0 ]; then
echo "ERROR: PHPMailer installation failed!"
echo "Please report this to our development team:"
echo "https://github.com/hestiacp/hestiacp/issues"
$BIN/v-add-user-notification admin 'Hestia PHP dependencies installation failed!' '<p>Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a> to report this to our development team.</p>'
# Installation failed, clean up files
rm --recursive --force ${PM_INSTALL_DIR}/vendor
$BIN/v-change-sys-config-value 'USE_SERVER_SMTP' 'n'
$BIN/v-log-action "system" "Error" "Plugins" "PHP dependencies installation failed"
exit 1
fi
cd "$QUICK_INSTALL_DIR"
rm --recursive --force ${QUICK_INSTALL_DIR}/vendor
mkdir -p ${QUICK_INSTALL_DIR}/vendor
chown $user: -R ${QUICK_INSTALL_DIR}/vendor
if [ -z "$openssl_installed" ]; then
COMPOSER_HOME="$HOMEDIR/$user/.config/composer" user_exec /usr/bin/php $COMPOSER_BIN --quiet --no-dev install
else
COMPOSER_HOME="$HOMEDIR/$user/.config/composer" user_exec $HESTIA_PHP $COMPOSER_BIN --quiet --no-dev install
fi
# Set permissions
chown root: -R "${PM_INSTALL_DIR}/vendor"
chown root: -R "${QUICK_INSTALL_DIR}/vendor"
#----------------------------------------------------------#
# Logging #
#----------------------------------------------------------#
$BIN/v-log-action "system" "Info" "Plugins" "PHPMailer enabled (Version: $pm_v)."
log_event "$OK" "$ARGUMENTS"

@ -0,0 +1,119 @@
#!/bin/bash
# info: add file manager functionality to Hestia Control Panel
# options: [MODE]
#
# This function installs the File Manager on the server
# for access through the Web interface.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# load config file
source_conf "$HESTIA/install/upgrade/upgrade.conf"
MODE=$1
user="admin"
FM_INSTALL_DIR="$HESTIA/web/fm"
FM_FILE="filegator_latest"
FM_URL="https://github.com/filegator/static/raw/master/builds/filegator_latest.zip"
COMPOSER_BIN="$HOMEDIR/$user/.composer/composer"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
# Checking root permissions
if [ "x$(id -u)" != 'x0' ]; then
echo "ERROR: v-add-sys-filemanager can be run executed only by root user"
exit 10
fi
# Ensure that $HESTIA (/usr/local/hestia/) and other variables are valid.
if [ -z "$HESTIA" ]; then
HESTIA="/usr/local/hestia"
fi
if [ -z "$HOMEDIR" ] || [ -z "$HESTIA_INSTALL_DIR" ]; then
echo "ERROR: Environment variables not present, installation aborted."
exit 2
fi
# Ensure that Composer is installed for the user before continuing as it is a dependency of the File Manager.
if [ ! -f "$COMPOSER_BIN" ]; then
$BIN/v-add-user-composer "$user"
if [ $? -ne 0 ]; then
$BIN/v-add-user-notification admin 'Composer installation failed!' '<p class="u-text-bold">The File Manager will not work without Composer.</p><p>Please try running the installer manually from a shell session:<br><code>v-add-sys-filemanager</code></p><p>If this continues, <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.</p>'
exit 1
fi
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
openssl_installed=$(/usr/local/hestia/php/bin/php -m | grep openssl)
rm --recursive --force "$FM_INSTALL_DIR"
mkdir -p "$FM_INSTALL_DIR"
cd "$FM_INSTALL_DIR"
[ ! -f "${FM_INSTALL_DIR}/${FM_FILE}" ] && wget "$FM_URL" --quiet -O "${FM_INSTALL_DIR}/${FM_FILE}.zip"
unzip -qq "${FM_INSTALL_DIR}/${FM_FILE}.zip"
mv --force ${FM_INSTALL_DIR}/filegator/* "${FM_INSTALL_DIR}"
rm --recursive --force ${FM_INSTALL_DIR}/${FM_FILE}
[[ -f "${FM_INSTALL_DIR}/${FM_FILE}" ]] && rm "${FM_INSTALL_DIR}/${FM_FILE}"
cp --recursive --force ${HESTIA_INSTALL_DIR}/filemanager/filegator/* "${FM_INSTALL_DIR}"
chown $user: -R "${FM_INSTALL_DIR}"
if [ -z "$openssl_installed" ]; then
COMPOSER_HOME="$HOMEDIR/$user/.config/composer" user_exec /usr/bin/php $COMPOSER_BIN --quiet --no-dev install
else
COMPOSER_HOME="$HOMEDIR/$user/.config/composer" user_exec /usr/local/hestia/php/bin/php $COMPOSER_BIN --quiet --no-dev install
fi
# Check if installation was successful, if not abort script and throw error message notification and clean-up
if [ $? -ne 0 ]; then
echo "ERROR: File Manager installation failed!"
echo "Please report this to our development team:"
echo "https://github.com/hestiacp/hestiacp/issues"
$BIN/v-add-user-notification admin 'File Manager installation failed!' '<p>Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a> to report this to our development team.</p>'
# Installation failed, clean up files
rm --recursive --force ${FM_INSTALL_DIR}
$BIN/v-change-sys-config-value 'FILE_MANAGER' 'false'
$BIN/v-log-action "system" "Error" "Plugins" "File Manager installation failed (Version: $fm_v)."
exit 1
fi
# Add configuration file
cp -f $HESTIA_INSTALL_DIR/filemanager/filegator/configuration.php $HESTIA/web/fm/configuration.php
echo "$fm_v" > "${FM_INSTALL_DIR}/version"
# Set permissions
chown root: -R "${FM_INSTALL_DIR}"
chown $user: "${FM_INSTALL_DIR}/private"
chown $user: "${FM_INSTALL_DIR}/private/logs"
chown $user: "${FM_INSTALL_DIR}/repository"
$BIN/v-change-sys-config-value 'FILE_MANAGER' 'true'
#----------------------------------------------------------#
# Logging #
#----------------------------------------------------------#
$BIN/v-log-action "system" "Info" "Plugins" "File Manager enabled (Version: $fm_v)."
log_event "$OK" "$ARGUMENTS"

@ -0,0 +1,56 @@
#!/bin/bash
# info: add system firewall
# options: NONE
#
# example: v-add-sys-firewall
#
# This function enables the system firewall.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
if [ "$FIREWALL_SYSTEM" = 'iptables' ]; then
exit
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding default ruleset
if [ -z "$(ls -A $HESTIA/data/firewall 2> /dev/null)" ]; then
cp -rf $HESTIA_COMMON_DIR/firewall $HESTIA/data/
rm -f $HESTIA/data/firewall/ipset/blacklist.sh $HESTIA/data/firewall/ipset/blacklist.ipv6.sh
fi
# Updating FIREWAL_SYSTEM value
$BIN/v-change-sys-config-value "FIREWALL_SYSTEM" "iptables"
# Updating firewall rules
$BIN/v-update-firewall
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Info" "Firewall" "System firewall enabled."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,234 @@
#!/bin/bash
# info: add system IP address
# options: IP NETMASK [INTERFACE] [USER] [IP_STATUS] [IP_NAME] [NAT_IP]
#
# example: v-add-sys-ip 203.0.113.1 255.255.255.0
#
# This function adds IP address into a system. It also creates rc scripts. You
# can specify IP name which will be used as root domain for temporary aliases.
# For example, if you set a1.myhosting.com as name, each new domain created on
# this IP will automatically receive alias $domain.a1.myhosting.com. Of course
# you must have wildcard record *.a1.myhosting.com pointed to IP. This feature
# is very handy when customer wants to test domain before dns migration.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
ip="${1// /}"
netmask="$2"
# Get interface name
# First try to detect which interface the IP address resides on
iface="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[] | if .addr_info[].local == $IP then .ifname else empty end')"
# If that fails, detect the default interface as a fallback
if [ -z "$iface" ]; then
iface="$(ip -d -j route show | jq -r '.[] | if .dst == "default" then .dev else empty end')"
fi
iface="${3-$iface}"
user="${4-admin}"
ip_status="${5-shared}"
ip_name="$6"
nat_ip="$7"
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'IP NETMASK [INTERFACE] [USER] [STATUS] [NAME] [NATED_IP]'
is_format_valid 'ip' 'netmask' 'iface' 'user' 'ip_status'
is_ip_free
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
if [ -n "$ip_name" ]; then
is_format_valid 'ip_name'
fi
if [ -n "$nat_ip" ]; then
is_format_valid 'nat_ip'
fi
if [ "$user" != "admin" ]; then
ip_status="dedicated"
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
cidr="$(convert_netmask "$netmask")"
broadcast="$(get_broadcast "$ip" "$netmask")"
sys_ip_check="$(ip addr | grep -w "$ip")"
if [ -z "$sys_ip_check" ]; then
# Adding system IP
ip addr add "$ip/$cidr" dev "$iface" broadcast "$broadcast" label "$iface"
# Check if netplan is in use and generate configuration file
if [ -n "$(netplan generate --mapping "$iface" 2> /dev/null | grep networkd)" ]; then
netplan="true"
else
netplan="false"
fi
if [ "$netplan" = "true" ]; then
if [ -f "/etc/netplan/60-hestia.yaml" ]; then
sys_ip=" - $ip/$cidr"
else
sys_ip="# Added by Hestia, please do not edit the file manually!"
sys_ip="$sys_ip\nnetwork:"
sys_ip="$sys_ip\n version: 2"
sys_ip="$sys_ip\n renderer: networkd"
sys_ip="$sys_ip\n ethernets:"
sys_ip="$sys_ip\n $iface:"
sys_ip="$sys_ip\n addresses:"
sys_ip="$sys_ip\n - $ip/$cidr"
fi
IFS='%'
echo -e "$sys_ip" >> /etc/netplan/60-hestia.yaml
unset IFS
else
sys_ip="\n# Added by Hestia Control Panel"
sys_ip="$sys_ip\nauto $iface"
sys_ip="$sys_ip\niface $iface inet static"
sys_ip="$sys_ip\naddress $ip"
sys_ip="$sys_ip\nnetmask $netmask"
echo -e $sys_ip >> /etc/network/interfaces
fi
fi
# Generating timestamp
new_timestamp
# Adding Hestia IP
echo "OWNER='$user'
STATUS='$ip_status'
NAME='$ip_name'
U_SYS_USERS=''
U_WEB_DOMAINS='0'
INTERFACE='$iface'
NETMASK='$netmask'
NAT='$nat_ip'
TIME='$time'
DATE='$date'" > $HESTIA/data/ips/$ip
chmod 660 $HESTIA/data/ips/$ip
# WEB support
if [ -n "$WEB_SYSTEM" ]; then
web_conf="/etc/$WEB_SYSTEM/conf.d/$ip.conf"
rm -f "$web_conf"
if [ "$WEB_SYSTEM" = 'httpd' ] || [ "$WEB_SYSTEM" = 'apache2' ]; then
if [ -z "$(/usr/sbin/apachectl -v | grep Apache/2.4)" ]; then
echo "NameVirtualHost $ip:$WEB_PORT" > "$web_conf"
fi
echo "Listen $ip:$WEB_PORT" >> "$web_conf"
cat $HESTIA_INSTALL_DIR/apache2/unassigned.conf >> "$web_conf"
sed -i 's/directIP/'$ip'/g' "$web_conf"
sed -i 's/directPORT/'$WEB_PORT'/g' "$web_conf"
elif [ "$WEB_SYSTEM" = 'nginx' ]; then
cp -f $HESTIA_INSTALL_DIR/nginx/unassigned.inc "$web_conf"
sed -i 's/directIP/'$ip'/g' "$web_conf"
process_http2_directive "$web_conf"
fi
if [ "$WEB_SSL" = 'mod_ssl' ]; then
if [ -z "$(/usr/sbin/apachectl -v | grep Apache/2.4)" ]; then
sed -i "1s/^/NameVirtualHost $ip:$WEB_SSL_PORT\n/" "$web_conf"
fi
sed -i "1s/^/Listen $ip:$WEB_SSL_PORT\n/" "$web_conf"
sed -i 's/directSSLPORT/'$WEB_SSL_PORT'/g' "$web_conf"
fi
fi
# Proxy support
if [ -n "$PROXY_SYSTEM" ]; then
cat $WEBTPL/$PROXY_SYSTEM/proxy_ip.tpl \
| sed -e "s/%ip%/$ip/g" \
-e "s/%web_port%/$WEB_PORT/g" \
-e "s/%proxy_port%/$PROXY_PORT/g" \
-e "s/%proxy_ssl_port%/$PROXY_SSL_PORT/g" \
> /etc/$PROXY_SYSTEM/conf.d/$ip.conf
process_http2_directive "/etc/$PROXY_SYSTEM/conf.d/$ip.conf"
# mod_extract_forwarded
fw_conf="/etc/$WEB_SYSTEM/conf.d/mod_extract_forwarded.conf"
if [ -e "$fw_conf" ]; then
ips=$(grep 'MEFaccept ' "$fw_conf" | grep -v '#' | head -n1)
sed -i "s/$ips/$ips $ip/g" "$fw_conf"
fi
# mod_rpaf
rpaf_conf="/etc/$WEB_SYSTEM/mods-enabled/rpaf.conf"
if [ -e "$rpaf_conf" ]; then
rpaf_str="$(grep RPAFproxy_ips "$rpaf_conf")"
[ -z "$rpaf_str" ] && sed -i 's|</IfModule>|RPAFproxy_ips\n</IfModule>|' "$rpaf_conf" && rpaf_str='RPAFproxy_ips'
rpaf_str="$rpaf_str $ip"
sed -i "s/.*RPAFproxy_ips.*/$rpaf_str/" "$rpaf_conf"
fi
# mod_remoteip
remoteip_conf="/etc/$WEB_SYSTEM/mods-enabled/remoteip.conf"
if [ -e "$remoteip_conf" ]; then
if [ "$(grep -ic "$ip" "$remoteip_conf")" -eq "0" ]; then
sed -i "s/<\/IfModule>/RemoteIPInternalProxy $ip\n<\/IfModule>/g" "$remoteip_conf"
fi
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Updating user counters
increase_user_value "$user" '$IP_OWNED'
if [ "$user" = 'admin' ]; then
if [ "$ip_status" = 'shared' ]; then
for hestia_user in $($BIN/v-list-sys-users plain); do
increase_user_value "$hestia_user" '$IP_AVAIL'
done
else
increase_user_value 'admin' '$IP_AVAIL'
fi
else
increase_user_value "$user" '$IP_AVAIL'
increase_user_value 'admin' '$IP_AVAIL'
fi
# Restarting web server
$BIN/v-restart-web
check_result $? "Web restart failed" > /dev/null
# Restarting proxy server
if [ -n "$PROXY_SYSTEM" ]; then
$BIN/v-restart-proxy
check_result $? "Proxy restart failed" > /dev/null
fi
# Restarting firewall
if [ -n "$FIREWALL_SYSTEM" ]; then
$BIN/v-update-firewall
fi
# Logging
$BIN/v-log-action "system" "Info" "Network" "Added new IP address to the system (IP: $ip)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,120 @@
#!/bin/bash
# info: enables support for single sign on phpMyAdmin
# options: [mode]
#
# example: v-add-sys-pma-sso
#
# This function enables support for SSO to phpMyAdmin
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
MODE=$1
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
PMA_INSTALL="/usr/share/phpmyadmin"
PMA_CONFIG="/etc/phpmyadmin"
WWW_USER="www-data"
if [ -f /etc/redhat-release ]; then
WWW_USER="apache"
fi
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
# Checking root permissions
if [ "x$(id -u)" != 'x0' ]; then
echo "Error: Script can be run executed only by root"
exit 10
fi
if [ -n "$PHPMYADMIN_KEY" ] && [ "$PHPMYADMIN_KEY" != "" ]; then
echo "Error: SSO has been installed before to reenable it please run v-delete-sys-pma-sso first"
exit 1
fi
if [ -f "/usr/share/phpmyadmin/hestia-sso.php" ]; then
echo "Error: hestia-sso.php is already installed"
exit 2
fi
if [ -f "/usr/local/hesta/web/api/index.php" ]; then
echo "Error: API script not installed"
exit 2
fi
if [ "API_SYSTEM" = "0" ]; then
echo "Error: API is not enabled"
exit 2
fi
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generate the keys to secure everything
phpmyadminkey=$(generate_password)
apikey=$($BIN/v-add-access-key 'admin' 'phpmyadmin-sso' 'phpMyAdmin' 'plain')
# copy config dir to /usr/share/phpmyadmin/
cp -f $HESTIA_COMMON_DIR/phpmyadmin/hestia-sso.php $PMA_INSTALL/hestia-sso.php
chmod 640 $PMA_INSTALL/hestia-sso.php
chown root:$WWW_USER $PMA_INSTALL/hestia-sso.php
sed -i "s/%PHPMYADMIN_KEY%/$phpmyadminkey/g" $PMA_INSTALL/hestia-sso.php
sed -i "s/%API_KEY%/$apikey/g" $PMA_INSTALL/hestia-sso.php
sed -i "s/%API_HOST_NAME%/$(hostname)/g" $PMA_INSTALL/hestia-sso.php
sed -i "s/%API_HESTIA_PORT%/$BACKEND_PORT/g" $PMA_INSTALL/hestia-sso.php
# Check if config already contains the keys
touch $PMA_CONFIG/hestia-sso.inc.php
chmod 640 $PMA_CONFIG/hestia-sso.inc.php
chown root:$WWW_USER $PMA_CONFIG/hestia-sso.inc.php
echo "<?php
if(isset(\$_GET['hestia_token']) || isset(\$_COOKIE['SignonSession'])){
\$cfg['Servers'][\$i]['auth_type'] = 'signon';
\$cfg['Servers'][\$i]['SignonSession'] = 'SignonSession';
\$cfg['Servers'][\$i]['SignonURL'] = 'hestia-sso.php';
\$cfg['Servers'][\$i]['LogoutURL'] = 'hestia-sso.php?logout=1';
}
?>" >> $PMA_CONFIG/hestia-sso.inc.php
file=$(cat $PMA_CONFIG/config.inc.php)
if ! [[ "$file" =~ hestia-sso.inc.php ]]; then
if [[ $file =~ "//Add Hestia SSO code here" ]]; then
sed -i "s|//Add Hestia SSO code here|//Add Hestia SSO code here\n include ('$PMA_CONFIG/hestia-sso.inc.php');|g" $PMA_CONFIG/config.inc.php
else
echo "include ('$PMA_CONFIG/hestia-sso.inc.php');" >> $PMA_CONFIG/config.inc.php
fi
fi
$BIN/v-change-sys-config-value 'PHPMYADMIN_KEY' "$phpmyadminkey"
if [ -z "$(echo $API_ALLOWED_IP | grep 127.0.0.1)" ]; then
$BIN/v-add-sys-api-ip "127.0.0.1"
fi
#----------------------------------------------------------#
# Logging #
#----------------------------------------------------------#
if [ "$MODE" != "quiet" ]; then
echo "PMA Hestia-SSO plugin has been successfully installed"
fi
$BIN/v-log-action "system" "Info" "Plugins" "phpMyAdmin Single Sign-On has been enabled."
log_event "$OK" "$ARGUMENTS"

@ -0,0 +1,106 @@
#!/bin/bash
# info: add system quota
# options: NONE
#
# example: v-add-sys-quota
#
# This function enables filesystem quota on /home partition
# Some kernels do require additional packages to be installed first
#----------------------------------------------------------#
# Variable & Function #
#----------------------------------------------------------#
# Includes
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
# Ensure that quota kernel modules are installed
kernel_module_check=$(find /lib/modules/$(uname -r) -type f -name '*quota_v*.ko*' | egrep '.*' && [ $? -eq 0 ])
if [ -z "$kernel_module_check" ]; then
# Install kernel modules for quota support.
# Requires reboot to activate updated kernel.
echo "Installing required kernel modules for quota support..."
reboot_req="Y"
apt-get -qq install linux-image-extra-virtual -y
check_result $? "kernel module installation failed" "$E_UPDATE"
fi
# Checking quota package
quota=$(which --skip-alias --skip-functions quota 2> /dev/null)
if [ $? -ne 0 ]; then
if [ -f "/etc/redhat-release" ]; then
dnf -y install quota > /dev/null 2>&1
else
export DEBIAN_FRONTEND=noninteractive
apt-get -y install quota > /dev/null 2>&1
fi
check_result $? "quota package installation failed" "$E_UPDATE"
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding group and user quota on /home partition
mnt=$(df -P /home | awk '{print $6}' | tail -n1)
lnr=$(cat -n /etc/fstab | grep -v "#" | awk '{print $1,$3}' | grep "$mnt$" | cut -f 1 -d ' ')
opt=$(sed -n ${lnr}p /etc/fstab | awk '{print $4}')
fnd='usrquota\|grpquota\|usrjquota=aquota.user\|grpjquota=aquota.group\|jqfmt=vfsv0'
if [ $(echo $opt | tr ',' '\n' | grep -x $fnd | wc -l) -ne 5 ]; then
old=$(echo $(echo $opt | tr ',' '\n' | grep -v 'usrquota\|grpquota\|usrjquota=\|grpjquota=\|jqfmt=') | tr ' ' ',')
new='usrquota,grpquota,usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv0'
sed -i "$lnr s/$opt/$old,$new/" /etc/fstab
mount -o remount "$mnt"
fi
# Adding v2 group and user quota index
if [ ! -e "$mnt/aquota.user" ] || [ ! -e "$mnt/aquota.group" ]; then
quotacheck -avcugm > /dev/null 2>&1
fi
# Adding quotacheck on reboot
touch /forcequotacheck
# Adding cron job
echo '#!/bin/bash' > /etc/cron.daily/quotacheck
echo 'touch /forcequotacheck' >> /etc/cron.daily/quotacheck
chmod a+x /etc/cron.daily/quotacheck
# Enabling group and user quota
if [ -n "$(quotaon -pa | grep " $mnt " | grep 'user\|group' | grep 'is off')" ]; then
quotaon -v $mnt
check_result $? "quota can't be enabled in $mnt" "$E_DISK"
fi
# Updating hestia.conf value
if [ -z "$(grep DISK_QUOTA $HESTIA/conf/hestia.conf)" ]; then
echo "DISK_QUOTA='yes'" >> $HESTIA/conf/hestia.conf
else
sed -i "s/DISK_QUOTA=.*/DISK_QUOTA='yes'/g" $HESTIA/conf/hestia.conf
fi
# Rebuilding user quota
for user in $($BIN/v-list-sys-users plain); do
$BIN/v-update-user-quota "$user"
done
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Info" "Plugins" "System Quota enforcement enabled."
log_history "system quota enforcement enabled"
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,216 @@
#!/bin/bash
# info: Install Roundcube webmail client
# options: [MODE]
#
# This function installs the Roundcube webmail client.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
source $HESTIA/func/db.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# upgrade config file
source "$HESTIA/install/upgrade/upgrade.conf"
MODE=$2
UPDATE="no"
# Version and Download paths
RC_FILE="roundcubemail-$rc_v-complete.tar.gz"
RC_EXTRACT="roundcubemail-$rc_v"
# Downloading full version
RC_URL="https://github.com/roundcube/roundcubemail/releases/download/$rc_v/roundcubemail-$rc_v-complete.tar.gz"
# Folder paths
RC_INSTALL_DIR="/var/lib/roundcube"
RC_CONFIG_DIR="/etc/roundcube"
RC_LOG="/var/log/roundcube"
WWW_USER="www-data"
if [ -f /etc/redhat-release ]; then
WWW_USER="apache"
fi
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
# Checking root permissions
if [ "x$(id -u)" != 'x0' ]; then
echo "ERROR: v-add-sys-roundcube can be run executed only by root user"
exit 10
fi
# Ensure that $HESTIA (/usr/local/hestia/) and other variables are valid.
if [ -z "$HESTIA" ]; then
HESTIA="/usr/local/hestia"
fi
if [ -z "$HOMEDIR" ] || [ -z "$HESTIA_COMMON_DIR" ]; then
echo "ERROR: Environment variables not present, installation aborted."
exit 2
fi
if [ -z "$(echo "$DB_SYSTEM" | grep -w 'mysql')" ]; then
echo "ERROR: Mysql not available. Installation aborted"
exit 2
fi
if [ -d "/usr/share/roundcube" ]; then
echo "ERROR: Install done from apt source, unable to continue"
exit 2
fi
# Get current version
if [ -f "/var/lib/roundcube/index.php" ]; then
version=$(cat $RC_INSTALL_DIR/index.php | grep -o -E '[0-9].[0-9].[0-9]+' | head -1)
if [ "$version" == "$rc_v" ]; then
echo "Error: Installed version ($version) is equal to the available version ($rc_v)"
exit 2
else
UPDATE="yes"
fi
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
if [ "$UPDATE" == "no" ]; then
rm -f -r $RC_INSTALL_DIR
rm -f -r $RC_CONFIG_DIR
mkdir -p $RC_INSTALL_DIR/
mkdir -p $RC_CONFIG_DIR/
cd "$RC_INSTALL_DIR"
[ ! -f "${RC_INSTALL_DIR}/${RC_FILE}" ] && wget "$RC_URL" --retry-connrefused --quiet -O "${RC_INSTALL_DIR}/${RC_FILE}"
tar xzf $RC_FILE
cp -rT $RC_EXTRACT $RC_INSTALL_DIR
# Delete old config folder
cp $RC_INSTALL_DIR/config/defaults.inc.php $RC_CONFIG_DIR/defaults.inc.php
rm -f -r $RC_INSTALL_DIR/config/
ln -s $RC_CONFIG_DIR/ ./config
# Replace with Hestia config
cp -f $HESTIA_COMMON_DIR/roundcube/main.inc.php $RC_CONFIG_DIR/config.inc.php
cp -f $HESTIA_COMMON_DIR/roundcube/mimetypes.php $RC_CONFIG_DIR/mimetypes.php
chmod 644 $RC_CONFIG_DIR/*.php
cp -f $HESTIA_COMMON_DIR/roundcube/hestia.php $RC_INSTALL_DIR/plugins/password/drivers/
mkdir -p $RC_CONFIG_DIR/plugins/password
mkdir -p $RC_CONFIG_DIR/plugins/newmail_notifier
mkdir -p $RC_CONFIG_DIR/plugins/zipdownload
# Allow changes to the respective config / Create symlinks to /etc/roundcube/
cp -f $HESTIA_COMMON_DIR/roundcube/config.inc.php $RC_CONFIG_DIR/plugins/password/config.inc.php
ln -s $RC_CONFIG_DIR/plugins/password/config.inc.php ./plugins/password/config.inc.php
cp -f $HESTIA_COMMON_DIR/roundcube/plugins/config_newmail_notifier.inc.php $RC_CONFIG_DIR/plugins/newmail_notifier/config.inc.php
ln -s $RC_CONFIG_DIR/plugins/newmail_notifier/config.inc.php ./plugins/newmail_notifier/config.inc.php
cp -f $HESTIA_COMMON_DIR/roundcube/plugins/config_zipdownload.inc.php $RC_CONFIG_DIR/plugins/zipdownload/config.inc.php
ln -s $RC_CONFIG_DIR/plugins/zipdownload/config.inc.php ./plugins/zipdownload/config.inc.php
# Set up correct permissions roundcube
chown -R root:$WWW_USER $RC_CONFIG_DIR/
chmod 751 -R $RC_CONFIG_DIR
chmod 644 $RC_CONFIG_DIR/config.inc.php
chmod 644 $RC_CONFIG_DIR/plugins/password/config.inc.php
chmod 644 $RC_CONFIG_DIR/plugins/newmail_notifier/config.inc.php
chmod 644 $RC_CONFIG_DIR/plugins/zipdownload/config.inc.php
# Add robots.txt
echo "User-agent: *" > /var/lib/roundcube/robots.txt
echo "Disallow: /" >> /var/lib/roundcube/robots.txt
chown -R root:$WWW_USER $RC_INSTALL_DIR
# Log file
if [ ! -d $RC_LOG ]; then
mkdir $RC_LOG
fi
chown apache:$WWW_USER $RC_LOG
chmod 751 $RC_LOG
if [ ! -z "$(echo "$DB_SYSTEM" | grep -E 'mysql|pgsql')" ]; then
host='localhost'
database='roundcube'
dbuser="$database"
dbpass=$(generate_password)
charset='UTF8'
sed -i "s/%password%/$dbpass/g" $RC_CONFIG_DIR/config.inc.php
if [ ! -z "$(echo "$DB_SYSTEM" | grep -w 'mysql')" ]; then
add_mysql_database
mysql_query "USE $database; $(< /var/lib/roundcube/SQL/mysql.initial.sql)"
else
add_pgsql_database
psql_query "USE $database; $(< /var/lib/roundcube/SQL/postgres.initial.sql)"
fi
fi
# TODO: Add support for PostgreSQL
rcDesKey="$(openssl rand -base64 30 | tr -d "/" | cut -c1-24)"
sed -i "s/%des_key%/$rcDesKey/g" $RC_CONFIG_DIR/config.inc.php
# Update server hostname in password change plugin
sed -i "s/localhost/$(hostname)/g" $RC_CONFIG_DIR/plugins/password/config.inc.php
# Clean up
rm -f -r $RC_INSTALL_DIR/installer
rm -f -r $RC_INSTALL_DIR/$RC_FILE
rm -f -r $RC_INSTALL_DIR/$RC_EXTRACT
# Updating hestia.conf
if [ -z "$(grep WEBMAIL_SYSTEM $HESTIA/conf/hestia.conf)" ]; then
$BIN/v-change-sys-config-value 'WEBMAIL_SYSTEM' 'roundcube'
else
if [ -z "$(echo "$WEBMAIL_SYSTEM" | grep -w 'roundcube')" ]; then
if [ ! -z "$WEBMAIL_SYSTEM" ]; then
$BIN/v-change-sys-config-value 'WEBMAIL_SYSTEM' "roundcube,$WEBMAIL_SYSTEM"
else
$BIN/v-change-sys-config-value 'WEBMAIL_SYSTEM' "roundcube"
fi
fi
fi
phpenmod mcrypt > /dev/null 2>&1
else
cd "$RC_INSTALL_DIR"
[ ! -f "${RC_INSTALL_DIR}/${RC_FILE}" ] && wget "$RC_URL" --quiet -O "${RC_INSTALL_DIR}/${RC_FILE}"
tar xzf $RC_FILE
# Run Roundcube upgrade script
$RC_INSTALL_DIR/$RC_EXTRACT/bin/installto.sh -y $RC_INSTALL_DIR > /dev/null 2>&1
$RC_INSTALL_DIR/bin/update.sh --version "$version" > /dev/null 2>&1
$RC_INSTALL_DIR/bin/indexcontacts.sh > /dev/null 2>&1
chown -R root:$WWW_USER $RC_INSTALL_DIR
#clean up the mess
if [ -d "$RC_INSTALL_DIR/installer" ]; then
rm -f -r $RC_INSTALL_DIR/installer
fi
rm -f -r $RC_INSTALL_DIR/$RC_FILE
rm -f -r $RC_INSTALL_DIR/$RC_EXTRACT
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
if [ "$UPDATE" = "yes" ]; then
$BIN/v-log-action "system" "Info" "Plugins" "Roundcube updated (Version: $version)."
else
$BIN/v-log-action "system" "Info" "Plugins" "Roundcube enabled (Version: $version)."
fi
log_event "$OK" "$ARGUMENTS"

@ -0,0 +1,99 @@
#!/bin/bash
# info: add system sftp jail
# options: [RESTART]
#
# example: v-add-sys-sftp-jail yes
#
# This function enables sftp jailed environment.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
restart=$1
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Checking sshd directives
config='/etc/ssh/sshd_config'
sftp_n=$(grep -n "Subsystem.*sftp" $config | grep -v internal | grep -v ":#")
sftp_i=$(grep -n "^# Hestia SFTP Chroot" $config)
# Disabling normal sftp
if [ -n "$sftp_n" ]; then
fline=$(echo $sftp_n | cut -f 1 -d :)
sed -i "${fline}s/Subsystem.*sftp/#Subsystem sftp/" $config
restart='yes'
fi
# Enabling jailed sftp
if [ -z "$sftp_i" ]; then
echo " " >> $config
echo "# Hestia SFTP Chroot" >> $config
echo "Match User sftp_dummy99" >> $config
echo "ChrootDirectory %h" >> $config
echo " X11Forwarding no" >> $config
echo " AllowTCPForwarding no" >> $config
echo " ForceCommand internal-sftp" >> $config
restart='yes'
fi
# Validating opensshd config
if [ "$restart" = 'yes' ]; then
subj="OpenSSH restart failed"
email=$(grep CONTACT $HESTIA/data/users/admin/user.conf | cut -f 2 -d \')
/usr/sbin/sshd -t > /dev/null 2>&1
if [ "$?" -ne 0 ]; then
mail_text="OpenSSH can not be restarted. Please check config:
\n\n$(/usr/sbin/sshd -t)"
echo -e "$mail_text" | $SENDMAIL -s "$subj" $email
else
service ssh restart > /dev/null 2>&1
fi
fi
# Checking users
shells="rssh|nologin"
for user in $(grep "$HOMEDIR" /etc/passwd | egrep "$shells" | cut -f 1 -d:); do
# Include all users v-add-user-sftp-jail will handle it
$BIN/v-add-user-sftp-jail "$user" "no"
done
# Restart ssh service
if [ "$restart" = 'no' ]; then
# Skip restart of SSH daemon
echo "" > /dev/null 2>&1
else
service ssh restart > /dev/null 2>&1
fi
# Add v-add-sys-sftp-jail to startup
if [ ! -e "/etc/cron.d/hestia-sftp" ]; then
echo "@reboot root sleep 60 && /usr/local/hestia/bin/v-add-sys-sftp-jail > /dev/null" > /etc/cron.d/hestia-sftp
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,63 @@
#!/bin/bash
# info: Add SMTP Account for logging, notification and internal mail
# options: DOMAIN PORT SMTP_SECURITY USERNAME PASSWORD EMAIL
#
# example: v-add-sys-smtp example.com 587 STARTTLS test@domain.com securepassword test@example.com
#
# This function allows configuring a SMTP account for the server to use
# for logging, notification and warn emails etc.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
domain=$1
port=$2
smtp_security=$3
username=$4
password=$5
email=$6
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '6' "$#" 'DOMAIN PORT SMTP_SECURITY USERNAME PASSWORD EMAIL'
is_format_valid 'domain' 'port' 'email' 'password'
is_username_format_valid "$username" 'username'
format_no_quotes "$password" 'passowrd'
is_common_format_valid "$smtp_security" "SMTP_SECURITY"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
change_sys_value "USE_SERVER_SMTP" 'true'
change_sys_value "SERVER_SMTP_HOST" "$domain"
change_sys_value "SERVER_SMTP_PORT" "$port"
change_sys_value "SERVER_SMTP_SECURITY" "$smtp_security"
change_sys_value "SERVER_SMTP_USER" "$username"
change_sys_value "SERVER_SMTP_PASSWD" "$password"
change_sys_value "SERVER_SMTP_ADDR" "$email"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Info" "Mail" "Server SMTP enabled."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,64 @@
#!/bin/bash
# info: add system wide smtp relay support
# options: HOST [USERNAME] [PASSWORD] [PORT]
#
# example: v-add-sys-smtp-relay srv.smtprelay.tld uname123 pass12345
#
# This function adds system wide smtp relay support.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
host=$1
username=$2
password=$3
port=${4-587}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'HOST [USERNAME] [PASSWORD] [PORT]'
is_format_valid 'port' 'host' 'password'
is_username_format_valid "$username" 'username'
format_no_quotes "$password" 'passowrd'
is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
change_sys_value 'SMTP_RELAY' 'true'
change_sys_value 'SMTP_RELAY_HOST' "$host"
change_sys_value 'SMTP_RELAY_PORT' "$port"
change_sys_value 'SMTP_RELAY_USER' "$username"
cat > /etc/exim4/smtp_relay.conf << EOL
host:$host
port:$port
user:$username
pass:$password
EOL
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Info" "Mail" "Server-wide SMTP Relay enabled (Host: $host, Port: $port)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,174 @@
#!/bin/bash
# info: Install SnappyMail webmail client
# options: [MODE]
#
# This function installs the SnappyMail webmail client.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# upgrade config file
source "$HESTIA/install/upgrade/upgrade.conf"
MODE=$1
UPDATE="no"
# Version and Download paths
# Version to be moved to upgrade script
SM_FILE="snappymail-latest.tar.gz"
# For removal of folder
SM_EXTRACT_MAIN="snappymail"
# Downloading full version
SM_URL="https://snappymail.eu/repository/latest.tar.gz"
# Folder paths
SM_INSTALL_DIR="/var/lib/snappymail"
SM_CONFIG_DIR="/etc/snappymail"
SM_LOG="/var/log/snappymail"
WWW_USER="www-data"
if [ -f /etc/redhat-release ]; then
WWW_USER="apache"
fi
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
# Checking root permissions
if [ "x$(id -u)" != 'x0' ]; then
echo "ERROR: v-add-sys-snappymail can only be executed by the root user"
exit 10
fi
# Ensure that $HESTIA (/usr/local/hestia/) and other variables are valid.
if [ -z "$HESTIA" ]; then
HESTIA="/usr/local/hestia"
fi
if [ -z "$HOMEDIR" ] || [ -z "$HESTIA_INSTALL_DIR" ]; then
echo "ERROR: Environment variables not present, installation aborted."
exit 2
fi
if [ -z "$(echo "$DB_SYSTEM" | grep -w 'mysql')" ]; then
echo "ERROR: Mysql not available. Installation aborted"
exit 2
fi
# Get current version
if [ -f "/var/lib/snappymail/data/VERSION" ]; then
version=$(cat $SM_INSTALL_DIR/data/VERSION)
if [ "$version" == "$sm_v" ]; then
echo "Error: Installed version ($version) is equal to the available version ($sm_v)"
exit 2
else
UPDATE="yes"
fi
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
if [ "$UPDATE" == "no" ]; then
rm -f -r $SM_INSTALL_DIR
rm -f -r $SM_CONFIG_DIR
mkdir $SM_INSTALL_DIR
mkdir $SM_CONFIG_DIR
cd "$SM_INSTALL_DIR"
[ ! -f "${SM_INSTALL_DIR}/${SM_FILE}" ] && wget "$SM_URL" --retry-connrefused --quiet -O "${SM_INSTALL_DIR}/${SM_FILE}"
if [ ! -f "${SM_INSTALL_DIR}/${SM_FILE}" ]; then
echo "ERROR: Download failed, installation aborted."
exit 2
fi
# Get current version
key=$(openssl rand -hex 4)
admin_account="admin_$key"
admin_password=$(generate_password)
echo "Username: admin_$key" > ~/.snappymail
echo "Password: $admin_password" >> ~/.snappymail
echo "Secret key: admin_$key" >> ~/.snappymail
tar -xzf ${SM_INSTALL_DIR}/${SM_FILE}
mv ./data $SM_CONFIG_DIR/
ln -s $SM_CONFIG_DIR/data/ ./data
if [ -f '/usr/bin/mariadb' ]; then
mariadb -e "CREATE DATABASE snappymail" 2>&1
r=$(generate_password)
mariadb -e "GRANT ALL ON snappymail.*
TO snappymail@localhost IDENTIFIED BY '$r'"
else
mysql -e "CREATE DATABASE snappymail" 2>&1
r=$(generate_password)
mysql -e "GRANT ALL ON snappymail.*
TO snappymail@localhost IDENTIFIED BY '$r'"
fi
php -f $HESTIA_COMMON_DIR/snappymail/install.php "admin_$key" "$admin_password" "$r" "$BACKEND_PORT"
chown -R $WWW_USER:$WWW_USER ./data
chown -R $WWW_USER:$WWW_USER $SM_CONFIG_DIR/
rm ${SM_INSTALL_DIR}/${SM_FILE}
# Add robots.txt
echo "User-agent: *" > $SM_INSTALL_DIR/robots.txt
echo "Disallow: /" >> $SM_INSTALL_DIR/robots.txt
# Updating hestia.conf
if [ -z "$(grep WEBMAIL_SYSTEM $HESTIA/conf/hestia.conf)" ]; then
$BIN/v-change-sys-config-value 'WEBMAIL_SYSTEM' 'snappymail'
else
if [ -z "$(echo "$WEBMAIL_SYSTEM" | grep -w 'snappymail')" ]; then
if [ -n "$WEBMAIL_SYSTEM" ]; then
$BIN/v-change-sys-config-value 'WEBMAIL_SYSTEM' "snappymail,$WEBMAIL_SYSTEM"
else
$BIN/v-change-sys-config-value 'WEBMAIL_SYSTEM' "snappymail"
fi
fi
fi
else
[ ! -f "${SM_INSTALL_DIR}/${SM_FILE}" ] && wget "$SM_URL" --quiet -O "${SM_INSTALL_DIR}/${SM_FILE}"
version=$(cat $SM_INSTALL_DIR/data/VERSION)
tar -xzf snappymail-latest.tar.gz "data/VERSION" $SM_INSTALL_DIR/
version_source=$(cat $SM_INSTALL_DIR/VERSION)
# Check version inside .tar.gz file in case hestia didn't update yet
if [ "$version" != "$version_source" ]; then
tar -xzf ${SM_INSTALL_DIR}/${SM_FILE}
rm $SM_INSTALL_DIR/$SM_FILE
fi
rm ${SM_INSTALL_DIR}/VERSION
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
if [ "$UPDATE" = "yes" ]; then
$BIN/v-log-action "system" "Info" "Plugins" "SnappyMail updated (Version: $version)."
else
$BIN/v-log-action "system" "Info" "Plugins" "SnappyMail enabled (Version: $version)."
fi
log_event "$OK" "$ARGUMENTS"

@ -0,0 +1,286 @@
#!/bin/bash
# info: add system user
# options: USER PASSWORD EMAIL [PACKAGE] [NAME] [LASTNAME]
#
# example: v-add-user admin2 P4$$w@rD bgates@aol.com
#
# This function creates new user account.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
password=$2
HIDE=2
email=$3
package=${4-default}
name=$5
# Last name has been added for backward compatibility with WHMCS / Blesta VestaCP Plugins
if [ -n "$6" ]; then
name="$name $6"
fi
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
is_user_free() {
# these names may cause issues with MariaDB/MySQL database names and should be reserved:
# sudo has been added due to Privilege escalation as sudo group has always sudo permission
check_sysuser=$(php -r '$reserved_names=array("aria", "aria_log", "mysql", "mysql_upgrade", "ib", "ib_buffer",
"ddl", "ddl_recovery", "performance", "sudo"); if(in_array(strtolower($argv[1]), $reserved_names, true)){echo implode(", ", $reserved_names);}' "$user")
if [ -n "$check_sysuser" ]; then
check_result "$E_INVALID" "The user name '$user' is reserved and cannot be used. List of reserved names: $check_sysuser"
return
fi
check_sysuser=$(cut -f 1 -d : /etc/passwd | grep "^$user$")
if [ -n "$check_sysuser" ] || [ -e "$USER_DATA" ]; then
check_result "$E_EXISTS" "user $user exists"
fi
}
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER PASSWORD EMAIL [PACKAGE] [NAME] '
is_format_valid 'user' 'email' 'package'
if [ -n "$name" ]; then
is_format_valid 'name'
fi
is_user_free "$user"
is_password_valid
is_package_valid
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Parsing package data
pkg_data=$(cat $HESTIA/data/packages/$package.pkg | egrep -v "TIME|DATE")
# Checking shell
shell_conf=$(echo "$pkg_data" | grep 'SHELL' | cut -f 2 -d \')
shell=$(grep -w "$shell_conf" /etc/shells | head -n1)
# Adding user
/usr/sbin/useradd "$user" -s "$shell" -c "$email" -m -d "$HOMEDIR/$user" -U
check_result $? "user creation failed" "$E_INVALID"
# Adding password
echo "$user:$password" | /usr/sbin/chpasswd
if [ $? -ne 0 ]; then
# Delete user on failure
/usr/sbin/deluser "$user" > /dev/null 2>&1
echo "Error: Password not accepted due to PAM restrictions"
exit 2
fi
# Add a general group for normal users created by Hestia
if [ -z "$(grep ^hestia-users: /etc/group)" ]; then
groupadd --system "hestia-users"
fi
# Add membership to hestia-users group to non-admin users
if [ "$user" = "admin" ]; then
setfacl -m "g:admin:r-x" "$HOMEDIR/$user"
else
usermod -a -G "hestia-users" "$user"
setfacl -m "u:$user:r-x" "$HOMEDIR/$user"
fi
setfacl -m "g:hestia-users:---" "$HOMEDIR/$user"
# Building directory tree
mkdir $HOMEDIR/$user/conf
if [ -n "$WEB_SYSTEM" ]; then
mkdir $HOMEDIR/$user/conf/web $HOMEDIR/$user/web $HOMEDIR/$user/tmp
chmod 751 $HOMEDIR/$user/conf/web
chmod 700 $HOMEDIR/$user/tmp
chown $user:$user $HOMEDIR/$user/tmp
chown root:$user $HOMEDIR/$user/web/
fi
if [ -n "$MAIL_SYSTEM" ]; then
mkdir $HOMEDIR/$user/conf/mail $HOMEDIR/$user/mail
chmod 751 $HOMEDIR/$user/mail
chmod 755 $HOMEDIR/$user/conf/mail
fi
if [ -n "$DNS_SYSTEM" ]; then
if [ "$DNS_SYSTEM" = 'named' ]; then
dns_group='named'
else
dns_group='bind'
fi
mkdir $HOMEDIR/$user/conf/dns
chmod 771 $HOMEDIR/$user/conf/dns
chown root:$dns_group $HOMEDIR/$user/conf/dns
fi
# Create default writeable folders
mkdir $HOMEDIR/$user/.config \
$HOMEDIR/$user/.cache \
$HOMEDIR/$user/.local \
$HOMEDIR/$user/.composer \
$HOMEDIR/$user/.vscode-server \
$HOMEDIR/$user/.ssh \
$HOMEDIR/$user/.npm
chown $user:$user \
$HOMEDIR/$user/.config \
$HOMEDIR/$user/.cache \
$HOMEDIR/$user/.local \
$HOMEDIR/$user/.composer \
$HOMEDIR/$user/.vscode-server \
$HOMEDIR/$user/.ssh \
$HOMEDIR/$user/.npm
# Set permissions
chmod a+x $HOMEDIR/$user
chattr +i $HOMEDIR/$user/conf > /dev/null 2>&1
# Adding user dir
mkdir -p $USER_DATA/ssl $USER_DATA/dns $USER_DATA/mail
# Creating configuration files and pipes
touch $USER_DATA/backup.conf \
$USER_DATA/history.log \
$USER_DATA/stats.log \
$USER_DATA/web.conf \
$USER_DATA/dns.conf \
$USER_DATA/mail.conf \
$USER_DATA/db.conf \
$USER_DATA/cron.conf
chmod 770 $USER_DATA \
$USER_DATA/ssl \
$USER_DATA/dns \
$USER_DATA/mail
chmod 660 $USER_DATA/backup.conf \
$USER_DATA/history.log \
$USER_DATA/stats.log \
$USER_DATA/web.conf \
$USER_DATA/dns.conf \
$USER_DATA/mail.conf \
$USER_DATA/db.conf \
$USER_DATA/cron.conf
# Updating queue pipes
echo "$BIN/v-update-user-disk $user" >> $HESTIA/data/queue/disk.pipe
if [ -n "$WEB_SYSTEM" ]; then
echo "$BIN/v-update-web-domains-traff $user" \
>> $HESTIA/data/queue/traffic.pipe
echo "$BIN/v-update-web-domains-disk $user" >> $HESTIA/data/queue/disk.pipe
fi
if [ -n "$MAIL_SYSTEM" ]; then
echo "$BIN/v-update-mail-domains-disk $user" >> $HESTIA/data/queue/disk.pipe
fi
if [ -n "$DB_SYSTEM" ]; then
echo "$BIN/v-update-databases-disk $user" >> $HESTIA/data/queue/disk.pipe
fi
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Filling user config
if [ "$user" != 'admin' ]; then
ip_avail=$($BIN/v-list-user-ips admin plain | grep -w shared | wc -l)
u_users=0
else
ip_avail=0
u_users=1
fi
echo "NAME='$name'
PACKAGE='$package'
$pkg_data
CONTACT='$email'
CRON_REPORTS='yes'
MD5='$(awk -v user=$user -F : 'user == $1 {print $2}' /etc/shadow)'
RKEY='$(generate_password)'
TWOFA=''
QRCODE=''
PHPCLI=''
ROLE='user'
SUSPENDED='no'
SUSPENDED_USERS='0'
SUSPENDED_WEB='0'
SUSPENDED_DNS='0'
SUSPENDED_MAIL='0'
SUSPENDED_DB='0'
SUSPENDED_CRON='0'
IP_AVAIL='$ip_avail'
IP_OWNED='0'
U_USERS='$u_users'
U_DISK='0'
U_DISK_DIRS='0'
U_DISK_WEB='0'
U_DISK_MAIL='0'
U_DISK_DB='0'
U_BANDWIDTH='0'
U_WEB_DOMAINS='0'
U_WEB_SSL='0'
U_WEB_ALIASES='0'
U_DNS_DOMAINS='0'
U_DNS_RECORDS='0'
U_MAIL_DOMAINS='0'
U_MAIL_DKIM='0'
U_MAIL_ACCOUNTS='0'
U_MAIL_SSL='0'
U_DATABASES='0'
U_CRON_JOBS='0'
U_BACKUPS='0'
LANGUAGE=''
THEME=''
NOTIFICATIONS='no'
PREF_UI_SORT='name'
LOGIN_DISABLED='no'
LOGIN_USE_IPLIST='no'
LOGIN_ALLOW_IPS=''
TIME='$time'
DATE='$date'" > $USER_DATA/user.conf
chmod 660 $USER_DATA/user.conf
# Updating quota
if [ "$DISK_QUOTA" = 'yes' ]; then
$BIN/v-update-user-quota "$user"
fi
# Updating admin counter
if [ "$user" != 'admin' ]; then
increase_user_value 'admin' '$U_USERS'
fi
# Run template trigger
if [ -x "$HESTIA/data/packages/$package.sh" ]; then
$HESTIA/data/packages/$package.sh "$user" "$email" "$name"
fi
# Adding jailed sftp env
$BIN/v-add-user-sftp-jail $user
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Info" "Users" "User account added (Name: $user)."
$BIN/v-log-action "$user" "Info" "System" "Welcome!"
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,64 @@
#!/bin/bash
# info: add 2fa to existing user
# options: USER
#
# example: v-add-user-2fa admin
#
# This function creates a new 2fa token for user.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'USER'
is_format_valid 'user' 'system'
is_object_valid 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Reading user values
source $USER_DATA/user.conf
# Check if 2FA is already enabled
if [ -n "$TWOFA" ]; then
echo "Error: 2FA already enabled"
exit "$E_EXISTS"
fi
# Get secret and qr code from 2fa library
data=$($HESTIA_PHP $HESTIA/web/inc/2fa/secret.php)
# Split to secret and qrcode using delimiter
IFS='-' read -r -a array <<< "$data"
secret=${array[0]}
qrcode=${array[1]}
# Save the secret in user config (needs encryption?)
update_user_value "$user" '$TWOFA' "$secret"
update_user_value "$user" '$QRCODE' "$qrcode"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
exit

@ -0,0 +1,95 @@
#!/bin/bash
# info: add composer (php dependency manager) for a user
# options: USER
#
# example: v-add-user-composer user [version]
#
# This function adds support for composer (php dependency manager)
# Homepage: <https://getcomposer.org/>
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
version=${2-2}
if [ -z "$HESTIA" ]; then
HESTIA="/usr/local/hestia"
fi
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'USER [VERSION]'
is_format_valid 'user'
is_number_format_valid "$version" "version"
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
[ -z "$HOMEDIR" ] && check_result "$E_NOTEXIST" "Hestia environment vars not present"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
COMPOSER_DIR="$HOMEDIR/$user/.composer"
COMPOSER_BIN="$COMPOSER_DIR/composer"
if [ -f "$COMPOSER_BIN" ]; then
echo "Composer already available"
exit
fi
[ -z "$(readlink -m "$COMPOSER_DIR" | egrep "^$HOMEDIR/$user/")" ] && check_result "$E_FORBIDEN" "Path outside of user homedir (Composer dir)"
[ -z "$(readlink -m "$COMPOSER_BIN" | egrep "^$HOMEDIR/$user/")" ] && check_result "$E_FORBIDEN" "Path outside of user homedir (Composer bin)"
[ -z "$(readlink -m "$HOMEDIR/$user/.config/" | egrep "^$HOMEDIR/$user/")" ] && check_result "$E_FORBIDEN" "Path outside of user homedir (.conf)"
mkdir -p "$COMPOSER_DIR"
chown $user: "$COMPOSER_DIR"
mkdir -p "$HOMEDIR/$user/.config"
chown $user: "$HOMEDIR/$user/.config"
echo "alias composer=/home/"$user"/.composer/composer" >> /home/$user/.bash_aliases
COMPOSER_SETUP_FILE=$(mktemp)
check_result $? "Create temp file"
chown $user: "$COMPOSER_SETUP_FILE"
signature="$(curl --silent --show-error https://composer.github.io/installer.sig)"
check_result $? "Download signature"
user_exec wget --tries=3 --timeout=15 --read-timeout=15 --waitretry=3 --no-dns-cache https://getcomposer.org/installer --quiet -O "$COMPOSER_SETUP_FILE"
check_result $? "Download composer installer"
if [[ "$signature" != $(sha384sum "$COMPOSER_SETUP_FILE" | cut -f 1 -d " ") ]]; then
rm -f "$COMPOSER_SETUP_FILE"
check_result "$E_INVALID" "Composer signature does not match"
fi
COMPOSER_HOME="$HOMEDIR/$user/.config/composer" user_exec /usr/bin/php "$COMPOSER_SETUP_FILE" --quiet "--$version" --install-dir="$COMPOSER_DIR" --filename=composer
check_result $? "Composer install failed"
[ -f "$COMPOSER_SETUP_FILE" ] && rm -f "$COMPOSER_SETUP_FILE"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "$user" "Info" "Plugins" "Composer support enabled."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,79 @@
#!/bin/bash
# info: add user notification
# options: USER TOPIC NOTICE [TYPE]
#
# This function adds a new user notification to the panel.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
topic=$(echo $2 | sed "s/'/%quote%/g")
notice=$(echo $3 | sed "s/'/%quote%/g")
type=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER TOPIC NOTICE [TYPE]'
is_format_valid 'user' 'topic' 'notice'
is_object_valid 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Defining notification id
if [ -e "$USER_DATA/notifications.conf" ]; then
nid=$(grep "NID=" $USER_DATA/notifications.conf | cut -f 2 -d \')
nid=$(echo "$nid" | sort -n | tail -n1)
if [ -n "$nid" ]; then
nid="$((nid + 1))"
else
nid=1
fi
else
nid=1
fi
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Concatenating string
str="NID='$nid' TOPIC='$topic' NOTICE='$notice' TYPE='$type'"
str="$str ACK='no' TIME='$time' DATE='$date'"
# Adding to config
echo "$str" >> $USER_DATA/notifications.conf
# Changing permissions
chmod 660 $USER_DATA/notifications.conf
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Updating notification counter
if [ -z "$(grep NOTIFICATIONS $USER_DATA/user.conf)" ]; then
sed -i "s/^TIME/NOTIFICATIONS='yes'\nTIME/g" $USER_DATA/user.conf
else
update_user_value "$user" '$NOTIFICATIONS' "yes"
fi
exit

@ -0,0 +1,154 @@
#!/bin/bash
# info: adding user package
# options: tmpfile PACKAGE [REWRITE]
#
# This function adds new user package to the system.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
tmpfile=$1
package=$2
rewrite=$3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
is_package_consistent() {
source_conf "$tmpfile"
if [ "$WEB_DOMAINS" != 'unlimited' ]; then
is_int_format_valid "$WEB_DOMAINS" 'WEB_DOMAINS'
fi
if [ "$WEB_ALIASES" != 'unlimited' ]; then
is_int_format_valid "$WEB_ALIASES" 'WEB_ALIASES'
fi
if [ "$DNS_DOMAINS" != 'unlimited' ]; then
is_int_format_valid "$DNS_DOMAINS" 'DNS_DOMAINS'
fi
if [ "$DNS_RECORDS" != 'unlimited' ]; then
is_int_format_valid "$DNS_RECORDS" 'DNS_RECORDS'
fi
if [ "$MAIL_DOMAINS" != 'unlimited' ]; then
is_int_format_valid "$MAIL_DOMAINS" 'MAIL_DOMAINS'
fi
if [ "$MAIL_ACCOUNTS" != 'unlimited' ]; then
is_int_format_valid "$MAIL_ACCOUNTS" 'MAIL_ACCOUNTS'
fi
if [ "$DATABASES" != 'unlimited' ]; then
is_int_format_valid "$DATABASES" 'DATABASES'
fi
if [ "$CRON_JOBS" != 'unlimited' ]; then
is_int_format_valid "$CRON_JOBS" 'CRON_JOBS'
fi
is_int_format_valid "$RATE_LIMIT" 'RATE_LIMIT'
if [ "$DISK_QUOTA" != 'unlimited' ]; then
is_int_format_valid "$DISK_QUOTA" 'DISK_QUOTA'
fi
if [ "$BANDWIDTH" != 'unlimited' ]; then
is_int_format_valid "$BANDWIDTH" 'BANDWIDTH'
fi
is_int_format_valid "$BACKUPS" 'BACKUPS'
if [ -n "$WEB_TEMPLATE" ]; then
is_web_template_valid "$WEB_TEMPLATE"
fi
if [ -n "$BACKEND_TEMPLATE" ]; then
is_backend_template_valid "$BACKEND_TEMPLATE"
fi
if [ -n "$PROXY_TEMPLATE" ]; then
is_proxy_template_valid "$PROXY_TEMPLATE"
fi
if [ -n "$DNS_TEMPLATE" ]; then
is_dns_template_valid "$DNS_TEMPLATE"
fi
if [ -n "$NS" ]; then
IFS=',' read -r -a nameservers <<< "$NS"
i=1
for ns in "${nameservers[@]}"; do
is_domain_format_valid "$ns" "ns$i"
i=$((i + 1))
done
fi
is_format_valid_shell "$SHELL"
}
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'PKG_DIR PACKAGE' 'rewrite'
is_format_valid 'package'
if [ "$rewrite" != 'yes' ]; then
is_package_new "$package"
else
is_package_valid "$package"
fi
if [ ! -f "$tmpfile" ]; then
echo "$tmpfile does not exists"
exit "$E_NOTEXIST"
fi
is_package_consistent
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
SHELL=$(basename $SHELL)
echo "WEB_TEMPLATE='$WEB_TEMPLATE'
PROXY_TEMPLATE='$PROXY_TEMPLATE'
BACKEND_TEMPLATE='$BACKEND_TEMPLATE'
DNS_TEMPLATE='$DNS_TEMPLATE'
WEB_DOMAINS='$WEB_DOMAINS'
WEB_ALIASES='$WEB_ALIASES'
DNS_DOMAINS='$DNS_DOMAINS'
DNS_RECORDS='$DNS_RECORDS'
MAIL_DOMAINS='$MAIL_DOMAINS'
MAIL_ACCOUNTS='$MAIL_ACCOUNTS'
RATE_LIMIT='$RATE_LIMIT'
DATABASES='$DATABASES'
CRON_JOBS='$CRON_JOBS'
DISK_QUOTA='$DISK_QUOTA'
BANDWIDTH='$BANDWIDTH'
NS='$NS'
SHELL='$SHELL'
BACKUPS='$BACKUPS'
TIME='$time'
DATE='$date'
" > "$HESTIA/data/packages/$package.pkg"
chmod 644 "$HESTIA/data/packages/$package.pkg"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
if [ "$rewrite" != 'yes' ]; then
$BIN/v-log-action "system" "Info" "Packages" "Package added (Name: $package)."
else
$BIN/v-log-action "system" "Info" "Packages" "Package limits updated (Name: $package)."
fi
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,94 @@
#!/bin/bash
# info: add user sftp jail
# options: USER [RESTART]
#
# example: v-add-user-sftp-jail admin
#
# This function enables sftp jailed environment
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
restart=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'USER'
is_format_valid 'user'
check=$(is_object_valid 'user' 'USER' "$user")
if [ $? -ne 0 ]; then
user_str=$(grep "^$user:" /etc/passwd | egrep "rssh|nologin")
#try to detect "owner" of the ftp_user if not found dont set it up
user_owner=$(echo $user_str | cut -f6 -d : | cut -f3 -d /)
is_object_valid 'user' 'USER' "$user_owner"
fi
user_str=$(grep "^$user:" /etc/passwd | egrep "rssh|nologin")
if [ -z "$user_str" ]; then
exit
fi
# Get current users and split into array
ssh_users=$(grep -A1 "^# Hestia SFTP Chroot" /etc/ssh/sshd_config | sed -n 2p | sed 's/Match User //')
IFS=',' read -r -a users <<< "$ssh_users"
# Check if jail exist
match_string="$ssh_users,"
if [[ "$match_string" =~ ,$user, ]]; then
if [[ -d /home/$user && -z "$(find /home/$user -user root -print -prune -o -prune)" ]]; then
chown root:root /home/$user
fi
exit
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Add user to array
users+=($user)
# Write new user list to config
users=$(
IFS=','
echo "${users[*]// /|}"
IFS=$' \t\n'
)
sed -i "s/$ssh_users/$users/g" /etc/ssh/sshd_config
# Set home folder permission to root
if [ -d "/home/$user" ]; then
chown root:root /home/$user
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restart ssh service
if [ "$restart" = 'no' ]; then
# Skip restart of SSH daemon
echo "" > /dev/null 2>&1
else
service ssh restart > /dev/null 2>&1
fi
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,82 @@
#!/bin/bash
# info: add user sftp key
# options: USER [TTL]
#
# This function creates and updates SSH keys for used with the File Manager.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
ttl=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'USER [TTL]'
is_format_valid 'user' 'ttl'
is_object_valid 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
PRVKEY_FILE="$HOMEDIR/$user/.ssh/hst-filemanager-key"
PUBKEY_FILE="$HOMEDIR/$user/.ssh/hst-filemanager-key.pub"
AUTHKEY_FILE="$HOMEDIR/$user/.ssh/authorized_keys"
[ -z "$(readlink -f "$PRVKEY_FILE" | egrep "^$HOMEDIR/$user/.ssh/")" ] && check_result "$E_FORBIDEN" "Invalid private key file path"
[ -z "$(readlink -f "$PUBKEY_FILE" | egrep "^$HOMEDIR/$user/.ssh/")" ] && check_result "$E_FORBIDEN" "Invalid public key file path"
[ -z "$(readlink -f "$AUTHKEY_FILE" | egrep "^$HOMEDIR/$user/.ssh/")" ] && check_result "$E_FORBIDEN" "Invalid authorized keys path"
if [ ! -f "${PRVKEY_FILE}" ]; then
ssh-keygen -q -b 1024 -t rsa -f "${PRVKEY_FILE}" -N ""
rm "${PUBKEY_FILE}"
new_privkey=true
fi
if [ ! -f "${AUTHKEY_FILE}" ] || [ "$new_privkey" = true ]; then
pubkey_str="$(ssh-keygen -y -f ${PRVKEY_FILE})"
pubkey_desc="filemanager.ssh.key"
if grep --quiet --no-messages -F "$pubkey_desc" "${AUTHKEY_FILE}"; then
sed -i "/filemanager\.ssh\.key\$/d" "${AUTHKEY_FILE}"
fi
# make sure authorized_keys is ending with EOL
[ -f "${AUTHKEY_FILE}" ] && sed -i '$a\' "${AUTHKEY_FILE}"
expire=0
if [[ "$ttl" -gt 0 ]]; then
expire=$(date +%s -d "+${ttl} min")
echo "rm ${PRVKEY_FILE}" | at "now +${ttl} minute" > /dev/null 2>&1
fi
echo "from=\"127.0.0.1\",command=\"internal-sftp\",restrict ${pubkey_str} TS:${expire} ${pubkey_desc}" >> "${AUTHKEY_FILE}"
fi
#
chown ${user}: "${AUTHKEY_FILE}"
chown admin: "${PRVKEY_FILE}"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,73 @@
#!/bin/bash
# info: add ssh key
# options: USER KEY
#
# example: v-add-user-ssh-key user 'valid ssh key'
#
# Function check if $user/.ssh/authorized_keys exists and create it.
# After that it append the new key(s)
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
key=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER SSH_PUBLIC_KEY'
is_format_valid 'user'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Reading user values
source $USER_DATA/user.conf
AUTHKEY_FILE="$HOMEDIR/$user/.ssh/authorized_keys"
[ -z "$(readlink -f "$AUTHKEY_FILE" | egrep "^$HOMEDIR/$user/.ssh/")" ] && check_result "$E_FORBIDEN" "Invalid authorized keys path"
# Check if file exits
if [ ! -f "$AUTHKEY_FILE" ]; then
v-add-fs-file "$user" "${AUTHKEY_FILE}"
fi
[ -z "$key" ] && check_result "$E_NOTEXIST" "Empty ssh public key"
if ! echo "$key" | ssh-keygen -l -f - > /dev/null 2>&1; then
check_result "$E_PARSING" "Validating user private key"
fi
# Make sure authorized_keys ends with EOL
[ -f "${AUTHKEY_FILE}" ] && sed -i '$a\' "${AUTHKEY_FILE}"
# Append key data to file
echo "$key" >> "$AUTHKEY_FILE"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "$user" "Info" "System" "Added a new SSH key."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,69 @@
#!/bin/bash
# info: add wp-cli for a user
# options: USER
#
# example: v-add-user-wp-cli user
#
# This function adds support for wp-cli to the user account
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
if [ -z "$HESTIA" ]; then
HESTIA="/usr/local/hestia"
fi
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'USER'
is_format_valid 'user'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
WPCLI_DIR="/home/$user/.wp-cli"
WPCLI_BIN="$WPCLI_DIR/wp"
if [ -f "$WPCLI_BIN" ]; then
check_result "$E_EXISTS" "For user name '$user' WP-CLI already available!"
exit
fi
[ -z "$(readlink -m "$WPCLI_DIR" | egrep "^$HOMEDIR/$user/")" ] && check_result "$E_FORBIDEN" "Path outside of user homedir (WP Cli dir)"
[ -z "$(readlink -m "$WPCLI_BIN" | egrep "^$HOMEDIR/$user/")" ] && check_result "$E_FORBIDEN" "Path outside of user homedir (WP Cli bin)"
mkdir -p "$WPCLI_DIR"
chown $user:$user "$WPCLI_DIR"
user_exec wget --tries=3 --timeout=15 --read-timeout=15 --waitretry=3 --no-dns-cache --quiet -O "$WPCLI_BIN" https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
user_exec chmod +x "$WPCLI_BIN"
user_exec echo -e "#add wp-cli alias for user\nalias wp='php $WPCLI_BIN'" >> ~/.bashrc
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "$user" "Info" "Plugins" "WP-CLI support enabled."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,257 @@
#!/bin/bash
# info: add web domain
# options: USER DOMAIN [IP] [RESTART] [ALIASES] [PROXY_EXTENSIONS]
#
# example: v-add-web-domain admin wonderland.com 192.18.22.43 yes www.wonderland.com
#
# This function adds virtual host to a server. In cases when ip is
# undefined in the script, "default" template will be used. The alias of
# www.domain.tld type will be automatically assigned to the domain unless
# "none" is transmited as argument. If ip have associated dns name, this
# domain will also get the alias domain-tpl.$ipname. An alias with the ip
# name is useful during the site testing while dns isn't moved to server yet.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
ip=$3
restart=$4 # will be moved to the end soon
aliases=$5
proxy_ext=$6
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# shellcheck source=/usr/local/hestia/func/syshealth.sh
source $HESTIA/func/syshealth.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
format_aliases
domain_utf=$(idn2 --quiet -d "$domain_idn")
WWW_USER="www-data"
if [ -f /etc/redhat-release ]; then
WWW_USER="apache"
fi
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
check_args '2' "$#" 'USER DOMAIN [IP] [RESTART] [ALIASES] [PROXY_EXTENSIONS]'
is_format_valid 'user' 'domain' 'aliases' 'ip' 'proxy_ext'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_package_full 'WEB_DOMAINS'
if [ "$aliases" != "none" ]; then
ALIAS="$aliases"
is_package_full 'WEB_ALIASES'
fi
if [ "$($BIN/v-list-web-domain $user $domain_utf plain | cut -f 1) " != "$domain" ]; then
is_domain_new 'web' "$domain_utf,$aliases"
fi
if [ "$($BIN/v-list-web-domain $user $domain_idn plain | cut -f 1) " != "$domain" ]; then
is_domain_new 'web' "$domain_idn,$aliases"
else
is_domain_new 'web' "$domain,$aliases"
fi
if [ -z "$(is_ip_format_valid $domain)" ]; then
echo "Error: Invalid domain format. IP address detected as input."
exit 1
fi
is_dir_symlink "$HOMEDIR/$user/web"
is_dir_symlink "$HOMEDIR/$user/web/$domain"
is_base_domain_owner "$domain,$aliases"
if [ -n "$ip" ]; then
is_ip_valid "$ip" "$user"
else
get_user_ip
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Reading user values
source_conf "$USER_DATA/user.conf"
[[ -e "$HOMEDIR/$user/web/$domain" ]] && check_result "$E_EXISTS" "Web domain folder for $domain should not exist"
# Creating domain directories
mkdir $HOMEDIR/$user/web/$domain
chown $user:$user $HOMEDIR/$user/web/$domain
$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/public_html"
$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/document_errors"
$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/cgi-bin"
$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/private"
$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/stats"
$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/logs"
# Creating domain logs
touch /var/log/$WEB_SYSTEM/domains/$domain.bytes \
/var/log/$WEB_SYSTEM/domains/$domain.log \
/var/log/$WEB_SYSTEM/domains/$domain.error.log
ln -f -s /var/log/$WEB_SYSTEM/domains/$domain.*log \
$HOMEDIR/$user/web/$domain/logs/
# Adding domain skeleton
user_exec cp -r $WEBTPL/skel/* "$HOMEDIR/$user/web/$domain/" > /dev/null 2>&1
for file in $(find "$HOMEDIR/$user/web/$domain/" -type f); do
sed -i "s/%domain%/$domain/g" $file
done
# Changing file owner & permission
chown -R $user:$user $HOMEDIR/$user/web/$domain
chown root:$user /var/log/$WEB_SYSTEM/domains/$domain.* $conf
chmod 640 /var/log/$WEB_SYSTEM/domains/$domain.*
user_exec chmod 751 $HOMEDIR/$user/web/$domain/*
user_exec chmod 551 $HOMEDIR/$user/web/$domain/stats $HOMEDIR/$user/web/$domain/logs
user_exec chmod 644 $HOMEDIR/$user/web/$domain/public_*html/*
user_exec chmod 551 $HOMEDIR/$user/web/$domain
chown --no-dereference $user:$WWW_USER $HOMEDIR/$user/web/$domain/public_*html
# Adding PHP-FPM backend
if [ -n "$WEB_BACKEND" ]; then
if [ -z "$BACKEND_TEMPLATE" ]; then
BACKEND_TEMPLATE='default'
if [ -z "$(grep BACKEND_TEMPLATE $USER_DATA/user.conf)" ]; then
sed -i "s/^DNS_TEMPL/BACKEND_TEMPLATE='default'\nDNS_TEMPL/g" \
$USER_DATA/user.conf
else
update_user_value "$user" '$BACKEND_TEMPLATE' "default"
fi
fi
export BACKEND="$BACKEND_TEMPLATE"
$BIN/v-add-web-domain-backend "$user" "$domain" "$BACKEND_TEMPLATE" "$restart"
check_result $? "Backend error" > /dev/null
fi
# Preparing domain aliases
if [ "$aliases" = 'none' ]; then
ALIAS=''
else
ALIAS="www.$domain"
if [ -z "$aliases" ]; then
# Check and skip www alias for subdomains.
IFS='.' read -r -a domain_elements <<< "$domain"
if [ "${#domain_elements[@]}" -gt 2 ]; then
is_valid_2_part_extension $domain
if [ $? -ne 0 ]; then
ALIAS=""
else
ALIAS="www.$domain"
fi
else
ALIAS="www.$domain"
fi
else
ALIAS="$aliases"
fi
ip_alias=$(get_ip_alias "$domain")
if [ -n "$ip_alias" ]; then
ALIAS="$ALIAS,$ip_alias"
fi
fi
# Preparing domain variables
prepare_web_domain_values
if [ -z "$WEB_TEMPLATE" ]; then
WEB_TEMPLATE='default'
update_user_value "$user" '$WEB_TEMPLATE' "default"
fi
# Adding web server config
add_web_config "$WEB_SYSTEM" "$WEB_TEMPLATE.tpl"
# Adding proxy config
if [ -n "$PROXY_SYSTEM" ]; then
PROXY_EXT="$proxy_ext"
if [ -z "$proxy_ext" ]; then
# Code
PROXY_EXT="css,htm,html,js,json,xml"
# Image (from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types)
PROXY_EXT="$PROXY_EXT,apng,avif,bmp,cur,gif,ico,jfif,jpg,jpeg,pjp,pjpeg,png,svg,tif,tiff,webp"
# Audio from (https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Audio_codecs)
PROXY_EXT="$PROXY_EXT,aac,caf,flac,m4a,midi,mp3,ogg,opus,wav"
# Video (from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs)
PROXY_EXT="$PROXY_EXT,3gp,av1,avi,m4v,mkv,mov,mpg,mpeg,mp4,mp4v,webm"
# Fonts
PROXY_EXT="$PROXY_EXT,otf,ttf,woff,woff2"
# Productivity
PROXY_EXT="$PROXY_EXT,doc,docx,odf,odp,ods,odt,pdf,ppt,pptx,rtf,txt,xls,xlsx"
# Archive
PROXY_EXT="$PROXY_EXT,7z,bz2,gz,rar,tar,tgz,zip"
# Binaries
PROXY_EXT="$PROXY_EXT,apk,appx,bin,dmg,exe,img,iso,jar,msi"
fi
if [ -z "$PROXY_TEMPLATE" ]; then
PROXY_TEMPLATE='default'
update_user_value "$user" '$PROXY_TEMPLATE' "default"
fi
add_web_config "$PROXY_SYSTEM" "$PROXY_TEMPLATE.tpl"
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Increasing counters
increase_ip_value "$local_ip"
increase_user_value "$user" '$U_WEB_DOMAINS'
increase_user_value "$user" '$U_WEB_ALIASES' "$alias_number"
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Adding domain in web.conf
echo "DOMAIN='$domain' IP='$ip' IP6='' CUSTOM_DOCROOT='' ALIAS='$ALIAS' TPL='$WEB_TEMPLATE'\
SSL='no' SSL_FORCE='no' SSL_HOME='same' LETSENCRYPT='no' FTP_USER='' FTP_MD5=''\
BACKEND='$BACKEND_TEMPLATE' PROXY='$PROXY_TEMPLATE' PROXY_EXT='$PROXY_EXT'\
STATS='' STATS_USER='' STATS_CRYPT='' U_DISK='0' U_BANDWIDTH='0'\
SUSPENDED='no' TIME='$time' DATE='$date'" >> $USER_DATA/web.conf
syshealth_repair_web_config
# Restarting web server
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
# Restarting proxy server
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Added new web domain (Name: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,114 @@
#!/bin/bash
# info: add web domain alias
# options: USER DOMAIN ALIASES [RESTART]
#
# example: v-add-web-domain-alias admin acme.com www.acme.com yes
#
# This function adds one or more aliases to a domain (it is also called
# "domain parking"). This function supports wildcards *.domain.tpl.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
aliases=$3
restart="$4"
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
format_aliases
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
if [ -z "$aliases" ]; then
check_result "$E_INVALID" "Invalid alias format: empty"
fi
check_args '3' "$#" 'USER DOMAIN ALIASES [RESTART]'
is_format_valid 'user' 'domain' 'aliases'
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
is_domain_new 'web' "$aliases"
is_base_domain_owner "$aliases"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
get_domain_values 'web'
# Preparing domain values for the template substitution
local_ip=$(get_real_ip $IP)
if [ -z "$ALIAS" ]; then
ALIAS="$aliases"
else
ALIAS="$ALIAS,$aliases"
fi
prepare_web_domain_values
is_package_full 'WEB_ALIASES'
# Rebuilding vhost
del_web_config "$WEB_SYSTEM" "$TPL.tpl"
add_web_config "$WEB_SYSTEM" "$TPL.tpl"
if [ "$SSL" = 'yes' ]; then
del_web_config "$WEB_SYSTEM" "$TPL.stpl"
add_web_config "$WEB_SYSTEM" "$TPL.stpl"
fi
# Rebuilding proxy configuration
if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then
del_web_config "$PROXY_SYSTEM" "$PROXY.tpl"
add_web_config "$PROXY_SYSTEM" "$PROXY.tpl"
if [ "$SSL" = 'yes' ]; then
del_web_config "$PROXY_SYSTEM" "$PROXY.stpl"
add_web_config "$PROXY_SYSTEM" "$PROXY.stpl"
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding new alias
update_object_value 'web' 'DOMAIN' "$domain" '$ALIAS' "$ALIAS"
increase_user_value "$user" '$U_WEB_ALIASES'
# Restarting web server
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
# Restarting proxy server
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
$BIN/v-log-action "$user" "Info" "Web" "Added new web domain alias (Alias: $aliases, Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,71 @@
#!/bin/bash
# info: Allow other users create subdomains
# options: USER DOMAIN
#
# example: v-add-web-domain-allow-users admin admin.com
#
# Bypass the rule check for Enforce subdomain ownership for a specific domain.
# Enforce subdomain ownership setting in /edit/server/ set to no will always overwrite this behaviour
# eg: admin adds admin.com
# user can create user.admin.com
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN'
is_format_valid 'user' 'domain'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Load domain data
parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/web.conf)
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
if [ -z "$ALLOW_USERS" ]; then
add_object_key "web" 'DOMAIN' "$domain" 'ALLOW_USERS' 'TIME'
fi
# Adding new alias
update_object_value 'web' 'DOMAIN' "$domain" '$ALLOW_USERS' "yes"
$BIN/v-log-action "$user" "Warning" "Web" "Subdomain ownership enforcement disabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,97 @@
#!/bin/bash
# info: add web domain backend
# options: USER DOMAIN [TEMPLATE] [RESTART]
#
# example: v-add-web-domain-backend admin example.com default yes
#
# This function is used to add the web backend configuration.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
template=${3-default}
restart=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/conf/hestia.conf
source $HESTIA/conf/hestia.conf
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN [TEMPLATE] [RESTART]'
is_format_valid 'user' 'domain'
is_system_enabled "$WEB_BACKEND" 'WEB_BACKEND'
is_object_valid 'user' 'USER' "$user"
is_backend_template_valid "$template"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Defining pool directory
prepare_web_backend
get_domain_values 'web'
if [[ -n "$BACKEND" && "$BACKEND" != "$template" ]]; then
check_result "$E_EXISTS" "Pool already exists"
fi
# Allocating backend port
backend_port=9000
ports=$(grep -v '^;' $pool/* 2> /dev/null | grep listen | grep -o :[0-9].*)
ports=$(echo "$ports" | sed "s/://" | sort -n)
for port in $ports; do
if [ "$backend_port" -eq "$port" ]; then
backend_port=$((backend_port + 1))
fi
done
# Adding backend config
cat $WEBTPL/$WEB_BACKEND/$template.tpl \
| sed -e "s|%backend_port%|$backend_port|" \
-e "s|%user%|$user|g" \
-e "s|%domain%|$domain|g" \
-e "s|%backend%|$backend_type|g" \
-e "s|%backend_version%|$backend_version|g" > $pool/$backend_type.conf
# Set correct document root path
if [ -n "$CUSTOM_DOCROOT" ]; then
docroot="$CUSTOM_DOCROOT"
if [ -n "$CUSTOM_PHPROOT" ]; then
docroot="$CUSTOM_PHPROOT"
fi
sed -i "s|/home\/$user\/web\/$domain\/public_html|$docroot|g" $pool/$backend_type.conf
else
docroot="$HOMEDIR/$user/web/$domain/public_html/"
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restart php interpreter
$BIN/v-restart-web-backend "$restart" "$backend_version"
check_result $? "PHP restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Web domain configuration applied (Domain: $domain, Backend: $WEB_BACKEND)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,140 @@
#!/bin/bash
# info: add ftp account for web domain.
# options: USER DOMAIN FTP_USER FTP_PASSWORD [FTP_PATH]
#
# example: v-add-web-domain-ftp alice wonderland.com alice_ftp p4$$vvOrD
#
# This function creates additional ftp account for web domain.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
ftp_user=${1}_${3}
password=$4
HIDE=4
ftp_path=$5
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '4' "$#" 'USER DOMAIN FTP_USER FTP_PASSWORD [FTP_PATH]'
is_format_valid 'user' 'domain' 'ftp_user'
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
check_ftp_user=$(grep "^$ftp_user:" /etc/passwd)
if [ -n "$check_ftp_user" ] && [ "$FTP_USER" != "$ftp_user" ]; then
echo "Error: ftp user $ftp_user already exists"
log_event "$E_EXISTS" "$ARGUMENTS"
exit "$E_EXISTS"
fi
is_password_valid
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Get domain values
get_domain_values 'web'
# Defining ftp user shell
shell=$(which nologin)
if [ -n "$FTP_SHELL" ]; then
shell=$FTP_SHELL
fi
# Defining path
if [ -z "$ftp_path" ]; then
ftp_path_a="$HOMEDIR/$user/web/$domain"
else
# Validating absolute path
ftp_path_a=$(readlink -f "$HOMEDIR/$user/web/$domain/$ftp_path")
if [ -z "$(echo $ftp_path_a | grep $HOMEDIR/$user/web/$domain)" ]; then
echo "Error: absolute path $ftp_path_a is invalid"
log_event "$E_INVALID" "$ARGUMENTS"
exit "$E_INVALID"
fi
# Creating ftp user home directory
if [ ! -e "$ftp_path_a" ]; then
$BIN/v-add-fs-directory "$user" "$ftp_path_a"
chown $user:$user "$ftp_path_a"
chmod 751 "$ftp_path_a"
fi
fi
# Adding ftp user
/usr/sbin/useradd $ftp_user \
-s $shell \
-o -u $(id -u $user) \
-g $(id -g $user) \
-G hestia-users \
-M -d "$ftp_path_a" > /dev/null 2>&1
# Set ftp user password
echo "$ftp_user:$password" | /usr/sbin/chpasswd
if [ $? -ne 0 ]; then
# Delete user on failure again
/usr/sbin/deluser "$ftp_user" > /dev/null 2>&1
echo "Error: Password not accepted due to PAM restrictions"
exit 2
fi
ftp_md5=$(awk -v user=$ftp_user -F : 'user == $1 {print $2}' /etc/shadow)
# Adding jailed sftp env
$BIN/v-add-user-sftp-jail "$ftp_user"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Transforming absolute path to relative
ftp_path_r=$(echo $ftp_path_a | sed "s%$HOMEDIR/$user/web/$domain%%")
# Concatenating ftp variables
if [ ! -z "$FTP_USER" ]; then
ftp_user="$FTP_USER:$ftp_user"
ftp_md5="$FTP_MD5:$ftp_md5"
ftp_path="$FTP_PATH:$ftp_path_r"
fi
# Adding new key into web.conf
add_object_key "web" 'DOMAIN' "$domain" 'FTP_PATH' 'PROXY'
# Updating config
update_object_value 'web' 'DOMAIN' "$domain" '$FTP_USER' "$ftp_user"
update_object_value 'web' 'DOMAIN' "$domain" '$FTP_MD5' "$ftp_md5"
update_object_value 'web' 'DOMAIN' "$domain" '$FTP_PATH' "$ftp_path"
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Added new FTP account (Name: ${1}_${3}@$domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,125 @@
#!/bin/bash
# info: add password protection for web domain
# options: USER DOMAIN AUTH_USER AUTH_PASSWORD [RESTART]
#
# example: v-add-web-domain-httpauth admin acme.com user02 super_pass
#
# This function is used for securing web domain with http auth
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
auth_user=$3
password=$4
HIDE=4
restart=${5-yes}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Defining htpasswd file
htpasswd="$HOMEDIR/$user/conf/web/$domain/htpasswd"
docroot="$HOMEDIR/$user/web/$domain/public_html"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '4' "$#" 'USER DOMAIN AUTH_USER AUTH_PASSWORD [RESTART]'
is_format_valid 'user' 'domain'
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
is_password_valid
get_domain_values 'web'
is_user_format_valid "$auth_user" "Auth user"
if [ -n "$(echo "$AUTH_USER" | tr : '\n' | grep ^$auth_user$)" ]; then
echo "Error: auth user $auth_user already exists"
log_event "$E_EXISTS" "$ARGUMENTS"
exit "$E_EXISTS"
fi
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding htaccess password protection
if [ "$WEB_SYSTEM" = "nginx" ] || [ "$PROXY_SYSTEM" = "nginx" ]; then
htaccess="$HOMEDIR/$user/conf/web/$domain/nginx.conf_htaccess"
shtaccess="$HOMEDIR/$user/conf/web/$domain/nginx.ssl.conf_htaccess"
if [ ! -f "$htaccess" ]; then
echo "auth_basic \"$domain password access\";" > $htaccess
echo "auth_basic_user_file $htpasswd;" >> $htaccess
ln -s $htaccess $shtaccess
restart_required='yes'
fi
else
htaccess="$HOMEDIR/$user/conf/web/$domain/apache2.conf_htaccess"
shtaccess="$HOMEDIR/$user/conf/web/$domain/apache2.ssl.conf_htaccess"
if [ ! -f "$htaccess" ]; then
echo "<Directory $docroot>" > $htaccess
echo " AuthUserFile $htpasswd" >> $htaccess
echo " AuthName \"$domain access\"" >> $htaccess
echo " AuthType Basic" >> $htaccess
echo " Require valid-user" >> $htaccess
echo "</Directory>" >> $htaccess
ln -s $htaccess $shtaccess
restart_required='yes'
fi
fi
# Adding httpasswd user
auth_hash=$($BIN/v-generate-password-hash htpasswd htpasswd $password)
touch $htpasswd
chmod 644 $htpasswd $htaccess
chgrp $user $htpasswd $htaccess
sed -i "/^$auth_user:/d" $htpasswd
echo "$auth_user:$auth_hash" >> $htpasswd
# Restarting web server
if [ "$restart" != 'no' ] && [ "$restart_required" = 'yes' ]; then
$BIN/v-restart-web
if [ -n "$PROXY_SYSTEM" ]; then
$BIN/v-restart-proxy
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Preparing web.conf keys
if [ -n "$AUTH_USER" ]; then
auth_user="$AUTH_USER:$auth_user"
auth_hash="$AUTH_HASH:$auth_hash"
else
# Adding new key into web.conf
add_object_key "web" 'DOMAIN' "$domain" 'AUTH_USER' 'U_DISK'
add_object_key "web" 'DOMAIN' "$domain" 'AUTH_HASH' 'U_DISK'
fi
# Updating config
update_object_value 'web' 'DOMAIN' "$domain" '$AUTH_USER' "$auth_user"
update_object_value 'web' 'DOMAIN' "$domain" '$AUTH_HASH' "$auth_hash"
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Password protection enabled (Domain: $domain, Username: $httpauth_user)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,88 @@
#!/bin/bash
# info: add webdomain proxy support
# options: USER DOMAIN [TEMPLATE] [EXTENTIONS] [RESTART]
#
# example: v-add-web-domain-proxy admin example.com
#
# This function enables proxy support for a domain. This can significantly
# improve website speed.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
template=$3
default_extentions="jpg,jpeg,gif,png,webp,ico,svg,css,zip,tgz,gz,rar,bz2,doc,xls,\
exe,pdf,ppt,txt,odt,ods,odp,odf,tar,wav,bmp,rtf,js,mp3,avi,mpeg,flv,html,htm"
extentions=${4-$default_extentions}
restart="$5"
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN [TEMPLATE] [EXTENTIONS] [RESTART]'
is_format_valid 'user' 'domain' 'extentions'
is_system_enabled "$PROXY_SYSTEM" 'PROXY_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
is_object_value_empty 'web' 'DOMAIN' "$domain" '$PROXY'
if [ -z $template ]; then
template=$(get_user_value '$PROXY_TEMPLATE')
fi
is_proxy_template_valid $template
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Defining domain parameters
get_domain_values 'web'
prepare_web_domain_values
local_ip=$(get_real_ip "$IP")
# Preparing domain values for the template substitution
PROXY_EXT="$extentions"
add_web_config "$PROXY_SYSTEM" "$template.tpl"
# Adding proxy for ssl
if [ "$SSL" = 'yes' ]; then
add_web_config "$PROXY_SYSTEM" "$template.stpl"
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Update config
update_object_value 'web' 'DOMAIN' "$domain" '$PROXY' "$template"
update_object_value 'web' 'DOMAIN' "$domain" '$PROXY_EXT' "$extentions"
# Restarting web server
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
$BIN/v-log-action "$user" "Info" "Web" "Proxy enabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,162 @@
#!/bin/bash
# info: Adding force redirect to domain
# options: USER DOMAIN REDIRECT HTTPCODE [RESTART]
#
# example: v-add-web-domain-redirect user domain.tld domain.tld
# example: v-add-web-domain-redirect user domain.tld www.domain.tld
# example: v-add-web-domain-redirect user domain.tld shop.domain.tld
# example: v-add-web-domain-redirect user domain.tld different-domain.com
# example: v-add-web-domain-redirect user domain.tld shop.different-domain.com
# example: v-add-web-domain-redirect user domain.tld different-domain.com 302
#
# Function creates a forced redirect to a domain
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
redirect=$3
code=${4-301}
restart=${5-no}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN REDIRECT [HTTP-CODE] [RESTART]'
is_format_valid 'user' 'domain'
is_number_format_valid "$code" "code"
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
requesturi=0
if [[ "$3" =~ http://|https:// ]]; then
scheme_check=1
scheme=$($HESTIA_PHP -r '$url=parse_url($argv[1]); echo $url["scheme"];' "$redirect")
host=$($HESTIA_PHP -r '$url=parse_url($argv[1]); echo $url["host"];' "$redirect")
path=$($HESTIA_PHP -r '$url=parse_url($argv[1]); if(!empty($url["path"])){echo $url["path"];}' "$redirect")
port=$($HESTIA_PHP -r '$url=parse_url($argv[1]); if(!empty($url["port"])){echo $url["port"];}' "$redirect")
host=$(idn2 --quiet "$host")
redirect="$scheme://$host$path"
if [ -n "$port" ]; then
redirect="$scheme://$host:$port$path"
fi
isValidUrl=$(php -r '$url=$argv[1]; $url=filter_var($url,FILTER_VALIDATE_URL); echo $url;' "$redirect")
if [ -z "$isValidUrl" ]; then
check_result $E_INVALID "Invalid redirect"
fi
else
host=$($HESTIA_PHP -r '$url=parse_url($argv[1]); echo $url["host"];' "http://$redirect")
path=$($HESTIA_PHP -r '$url=parse_url($argv[1]); if(!empty($url["path"])){echo $url["path"];}' "http://$redirect")
port=$($HESTIA_PHP -r '$url=parse_url($argv[1]); if(!empty($url["port"])){echo $url["port"];}' "$redirect")
host=$(idn2 --quiet "$host")
redirect="$host$path"
if [ -n "$port" ]; then
redirect="$host:$port$path"
fi
isValidUrl=$(php -r '$url=$argv[1]; $url=filter_var($url,FILTER_VALIDATE_URL); echo $url;' "http://$redirect")
if [ -z "$isValidUrl" ]; then
check_result $E_INVALID "Invalid redirect"
fi
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Check if proxy is active
if [ "$WEB_SYSTEM" = 'nginx' ] || [ "$PROXY_SYSTEM" = 'nginx' ]; then
conf="$HOMEDIR/$user/conf/web/$domain/nginx.conf_redirect"
sconf="$HOMEDIR/$user/conf/web/$domain/nginx.ssl.conf_redirect"
fi
# Prevent $request_uri being added if ends in .html
requesturi=0
if [ -n "$path" ]; then
lastchr=${path#${path%?}}
if [ "$lastchr" = "/" ]; then
requesturi=1
redirect=${redirect:0:-1}
fi
else
requesturi=1
fi
# Insert redirect commands
if [ -n "$PROXY_SYSTEM" ] || [ "$WEB_SYSTEM" = 'nginx' ]; then
if [ "$scheme_check" = 1 ]; then
if [ "$requesturi" = 1 ]; then
echo " return $code $redirect\$request_uri;" > $conf
else
echo " return $code $redirect;" > $conf
fi
if [ ! -e "$sconf" ]; then
ln -s "$conf" "$sconf"
fi
else
echo "if (\$host != \"$redirect\") {" > $conf
if [ "$requesturi" = 1 ]; then
echo " return $code \$scheme://$redirect\$request_uri;" >> $conf
else
echo " return $code \$scheme://$redirect;" >> $conf
fi
echo "}" >> $conf
if [ ! -e "$sconf" ]; then
ln -s "$conf" "$sconf"
fi
fi
else
echo "Non supported please use .htaccess instead"
exit 2
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Update object keys
if [ -z "$REDIRECT" ]; then
add_object_key "web" 'DOMAIN' "$domain" 'REDIRECT' 'U_DISK'
add_object_key "web" 'DOMAIN' "$domain" 'REDIRECT_CODE' 'U_DISK'
fi
# Update values for domain
update_object_value 'web' 'DOMAIN' "$domain" '$REDIRECT' "$redirect"
update_object_value 'web' 'DOMAIN' "$domain" '$REDIRECT_CODE' "$code"
if [ "$restart" = "yes" ]; then
# Restarting web server
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
fi
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Domain redirection enabled (Domain: $domain, Redirect to: $redirect)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,143 @@
#!/bin/bash
# info: adding ssl for domain
# options: USER DOMAIN SSL_DIR [SSL_HOME] [RESTART]
#
# example: v-add-web-domain-ssl admin example.com /home/admin/conf/example.com/web
#
# This function turns on SSL support for a domain. Parameter ssl_dir is a path
# to directory where 2 or 3 ssl files can be found. Certificate file
# domain.tld.crt and its key domain.tld.key are mandatory. Certificate
# authority domain.tld.ca file is optional. If home directory parameter
# (ssl_home) is not set, https domain uses public_shtml as separate
# documentroot directory.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
ssl_dir=$3
ssl_home=${4-same}
restart="$5"
domain=$domain
domain_idn=$(idn2 --quiet "$domain")
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN SSL_DIR [SSL_HOME] [RESTART]'
is_format_valid 'user' 'domain' 'ssl_dir'
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
is_system_enabled "$WEB_SSL" 'SSL_SUPPORT'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
is_object_value_empty 'web' 'DOMAIN' "$domain" '$SSL'
is_web_domain_cert_valid
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Adding certificate to user data directory
cp -f $ssl_dir/$domain.crt $USER_DATA/ssl/$domain.crt
cp -f $ssl_dir/$domain.key $USER_DATA/ssl/$domain.key
cp -f $ssl_dir/$domain.crt $USER_DATA/ssl/$domain.pem
if [ -e "$ssl_dir/$domain.ca" ]; then
cp -f $ssl_dir/$domain.ca $USER_DATA/ssl/$domain.ca
echo >> $USER_DATA/ssl/$domain.pem
cat $USER_DATA/ssl/$domain.ca >> $USER_DATA/ssl/$domain.pem
fi
chmod 660 $USER_DATA/ssl/$domain.*
# Ensure SSL directory exists
if [ ! -d "$HOMEDIR/$user/conf/web/$domain/ssl" ]; then
mkdir -p $HOMEDIR/$user/conf/web/$domain/ssl/
fi
# Adding certificate to user dir
cp -f $USER_DATA/ssl/$domain.crt $HOMEDIR/$user/conf/web/$domain/ssl/$domain.crt
cp -f $USER_DATA/ssl/$domain.key $HOMEDIR/$user/conf/web/$domain/ssl/$domain.key
cp -f $USER_DATA/ssl/$domain.pem $HOMEDIR/$user/conf/web/$domain/ssl/$domain.pem
if [ -e "$USER_DATA/ssl/$domain.ca" ]; then
cp -f $USER_DATA/ssl/$domain.ca $HOMEDIR/$user/conf/web/$domain/ssl/$domain.ca
fi
if [ "$SSL_FORCE" == "yes" ]; then
# Enabling SSL redirection on demand
$BIN/v-add-web-domain-ssl-force "$user" "$domain"
fi
# Parsing domain values
get_domain_values 'web'
local_ip=$(get_real_ip "$IP")
# Preparing domain values for the template substitution
SSL_HOME="$ssl_home"
prepare_web_domain_values
# Adding domain to the web config
add_web_config "$WEB_SYSTEM" "$TPL.stpl"
# Checking proxy config
if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then
add_web_config "$PROXY_SYSTEM" "$PROXY.stpl"
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Increasing domain value
increase_user_value "$user" '$U_WEB_SSL'
# Adding ssl values
update_object_value 'web' 'DOMAIN' "$domain" '$SSL_HOME' "$SSL_HOME"
update_object_value 'web' 'DOMAIN' "$domain" '$SSL' "yes"
# Restarting web server
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
if [ -n "$UPDATE_HOSTNAME_SSL" ] && [ "$UPDATE_HOSTNAME_SSL" = "yes" ]; then
hostname=$(hostname -f)
if [ "$hostname" = "$domain" ]; then
$BIN/v-update-host-certificate "$user" "$domain"
fi
fi
if [ -n "$UPDATE_SSL_SCRIPT" ]; then
eval "$UPDATE_SSL_SCRIPT $user $domain"
fi
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Added certificate and enabled SSL (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,97 @@
#!/bin/bash
# info: Adding force SSL for a domain
# options: USER DOMAIN [RESTART] [QUIET]
#
# example: v-add-web-domain-ssl-force admin acme.com
#
# This function forces SSL for the requested domain.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
restart="$3"
quiet="$4"
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN'
is_format_valid 'user' 'domain'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Load domain data
parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/web.conf)
# Check if SSL is enabled
if [ "$SSL" != 'yes' ]; then
echo "Error: SSL is not enabled"
exit "$E_NOTEXIST"
fi
# Check if proxy is active
if [ -n "$PROXY_SYSTEM" ]; then
forcessl="$HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.forcessl.conf"
else
forcessl="$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.forcessl.conf"
fi
# Insert redirect commands
if [ -n "$PROXY_SYSTEM" ] || [ "$WEB_SYSTEM" = 'nginx' ]; then
echo 'return 301 https://$host$request_uri;' > $forcessl
else
echo 'RewriteEngine On' > $forcessl
echo 'RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]' >> $forcessl
fi
chown root:$user $forcessl
chmod 640 $forcessl
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
if [ -z "$SSL_FORCE" ]; then
add_object_key "web" 'DOMAIN' "$domain" 'SSL_FORCE' 'SSL_HOME'
fi
# Set forcessl flag to enabled
update_object_value 'web' 'DOMAIN' "$domain" '$SSL_FORCE' 'yes'
# Restart web server
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
# Restart proxy
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Logging
if [ "$quiet" != "yes" ]; then
$BIN/v-log-action "$user" "Info" "Web" "Automatic HTTPS redirection enabled (Domain: $domain)."
fi
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,89 @@
#!/bin/bash
# info: Adding hsts to a domain
# options: USER DOMAIN [RESTART] [QUIET]
#
# This function enables HSTS for the requested domain.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
restart="$3"
quiet="$4"
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN'
is_format_valid 'user' 'domain'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Load domain data
parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/web.conf)
# Check if SSL is enabled
if [ "$SSL" != 'yes' ]; then
echo "Error: SSL is not enabled"
exit "$E_NOTEXIST"
fi
# Check for Apache/Nginx or Nginx/PHP-FPM configuration
if [ -z "$PROXY_SYSTEM" ]; then
hstsconf="$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.hsts.conf"
else
hstsconf="$HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.hsts.conf"
fi
echo 'add_header Strict-Transport-Security "max-age=31536000;" always;' > $hstsconf
chown root:$user $hstsconf
chmod 640 $hstsconf
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
if [ -z "$SSL_HSTS" ]; then
add_object_key "web" 'DOMAIN' "$domain" 'SSL_HSTS' 'SSL_FORCE'
fi
# Set forcessl flag to enabled
update_object_value 'web' 'DOMAIN' "$domain" '$SSL_HSTS' 'yes'
# Restart web server
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
# Restart proxy
$BIN/v-restart-proxy "$restart"
check_result $? "Proxy restart failed" > /dev/null
# Logging
if [ "$quiet" != "yes" ]; then
$BIN/v-log-action "$user" "Info" "Web" "HTTP Strict Transport Security (HSTS) enabled (Domain: $domain)."
fi
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,60 @@
#!/bin/bash
# info: Adding force SSL for a domain
# options: USER DOMAIN [SSL]
#
# example: v-add-web-domain-ssl-preset
#
# Up on creating an web domain set the SSL Force values due to the delay of LE due to DNS propergation over DNS cluster
# When LE has been activated it will set the actions
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
ssl=$3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'USER DOMAIN [SSL]'
is_format_valid 'user' 'domain' 'ssl'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Load domain data
parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/web.conf)
if [ "$ssl" = "yes" ]; then
if [ -z "$SSL_FORCE" ]; then
add_object_key "web" 'DOMAIN' "$domain" 'SSL_FORCE' 'SSL_HOME'
fi
# Set forcessl flag to enabled
update_object_value 'web' 'DOMAIN' "$domain" '$SSL_FORCE' 'yes'
fi
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,109 @@
#!/bin/bash
# info: add log analyser to generate domain statistics
# options: USER DOMAIN TYPE
#
# example: v-add-web-domain-stats admin example.com awstats
#
# This function is used for enabling log analyser system to a domain. For viewing
# the domain statistics use <http://domain.tld/vstats/> link. Access this page
# is not protected by default. If you want to secure it with passwords you
# should use v-add-web-domain_stat_auth script.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
type=$3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN TYPE'
is_format_valid 'user' 'domain'
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
is_type_valid "$STATS_SYSTEM" "$type"
is_object_valid 'user' 'USER' "$user" "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
is_object_value_empty 'web' 'DOMAIN' "$domain" '$STATS'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Parse aliases
get_domain_values 'web'
# Adding statistic config
prepare_web_domain_values
if [ -z "${aliases//,/ }" ]; then
stats_alias="localhost"
else
stats_alias=${aliases_idn//,/ }
fi
cat $WEBTPL/$type/$type.tpl \
| sed -e "s|%ip%|$ip|g" \
-e "s|%web_port%|$WEB_PORT|g" \
-e "s|%web_system%|$WEB_SYSTEM|g" \
-e "s|%web_ssl_port%|$WEB_SSL_PORT|g" \
-e "s|%rgroups%|$WEB_RGROUPs|g" \
-e "s|%proxy_system%|$PROXY_SYSTEM|g" \
-e "s|%proxy_port%|$PROXY_PORT|g" \
-e "s|%proxy_ssl_port%|$PROXY_SSL_PORT|g" \
-e "s|%domain_idn%|$domain_idn|g" \
-e "s|%domain%|$domain|g" \
-e "s|%user%|$user|g" \
-e "s|%home%|$HOMEDIR|g" \
-e "s|%alias%|$stats_alias|g" \
-e "s|%alias_idn%|$stats_alias|g" \
> $HOMEDIR/$user/conf/web/$domain/$type.conf
if [ "$type" == 'awstats' ]; then
rm -f /etc/awstats/$type.$domain_idn.conf
ln -s $HOMEDIR/$user/conf/web/$domain/$type.conf \
/etc/awstats/$type.$domain_idn.conf
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Schedule statistic processing
echo "$BIN/v-update-web-domain-stat $user $domain" >> $HESTIA/data/queue/webstats.pipe
# Update config
update_object_value 'web' 'DOMAIN' "$domain" '$STATS' "$type"
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Web traffic analyzer enabled (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
# Build stats
exec $BIN/v-update-web-domain-stat "$user" "$domain"
exit

@ -0,0 +1,89 @@
#!/bin/bash
# info: add password protection to web domain statistics
# options: USER DOMAIN STATS_USER STATS_PASSWORD [RESTART]
#
# example: v-add-web-domain-stats-user admin example.com watchdog your_password
#
# This function is used for securing the web statistics page.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
stats_user=$3
password=$4
HIDE=4
restart=$5
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '4' "$#" 'USER DOMAIN STATS_USER STATS_PASS [RESTART]'
is_format_valid 'user' 'domain' 'stats_user'
is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'web' 'DOMAIN' "$domain"
is_object_unsuspended 'web' 'DOMAIN' "$domain"
is_password_valid
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Defining statistic dir
stats_dir="$HOMEDIR/$user/web/$domain/stats"
conf_dir="$HOMEDIR/$user/conf/web"
# Adding htaccess file
if [ "$WEB_SYSTEM" = 'nginx' ]; then
echo "auth_basic \"Web Statistics\";" > $stats_dir/auth.conf
echo "auth_basic_user_file $stats_dir/.htpasswd;" >> $stats_dir/auth.conf
else
echo "AuthUserFile $stats_dir/.htpasswd" > $stats_dir/.htaccess
echo "AuthName \"Web Statistics\"" >> $stats_dir/.htaccess
echo "AuthType Basic" >> $stats_dir/.htaccess
echo "Require valid-user" >> $stats_dir/.htaccess
fi
# Generating htaccess user and password
salt=$(generate_password "$PW_MATRIX" "8")
stats_pass=$($BIN/v-generate-password-hash md5 $salt $password)
echo "$stats_user:$stats_pass" > $stats_dir/.htpasswd
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Adding stats user in config
update_object_value 'web' 'DOMAIN' "$domain" '$STATS_USER' "$stats_user"
update_object_value 'web' 'DOMAIN' "$domain" '$STATS_CRYPT' "$stats_pass"
# Restarting web server
if [ "$WEB_SYSTEM" = 'nginx' ]; then
$BIN/v-restart-web "$restart"
check_result $? "Web restart failed" > /dev/null
fi
# Logging
$BIN/v-log-action "$user" "Info" "Web" "Web traffic analyzer password enabled (Username: $stats_user, Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,194 @@
#!/bin/bash
# info: add php fpm version
# options: VERSION
#
# example: v-add-web-php 8.0
#
# Install php-fpm for provided version.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
version=$1
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
source_conf "$HESTIA/install/upgrade/upgrade.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'VERSION'
if [ -z "$WEB_BACKEND" ]; then
echo "Multiple php versions are not supported for modphp"
fi
# Set file locations
php_fpm="/etc/init.d/php$version-fpm"
# Verify php version format
if [ -f /etc/redhat-release ]; then
if [[ ! $version =~ ^[0-9][0-9]+ ]]; then
echo "The specified PHP version format is invalid, it should look like [0-9][0-9]."
echo "Example: 70, 74, 80"
exit "$E_INVALID"
fi
else
if [[ ! $version =~ ^[0-9].[0-9]+ ]]; then
echo "The specified PHP version format is invalid, it should look like [0-9].[0-9]."
echo "Example: 7.0, 7.4, 8.0"
exit "$E_INVALID"
fi
fi
# Check if php version already exists
if [ -f "$php_fpm" ] && [ -f "$HESTIA/data/templates/web/php-fpm/PHP-${version/\./_}.tpl" ]; then
echo "ERROR: Specified PHP version is already installed."
exit "$E_INVALID"
fi
# Check if php version is supported
if [[ ! "$multiphp_v" =~ $version ]]; then
echo "ERROR: Specified PHP version is not supported or does not exist."
exit "$E_INVALID"
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
mph="php$version-common php$version-mbstring php$version-bcmath php$version-cli php$version-curl
php$version-fpm php$version-gd php$version-intl php$version-mysql
php$version-soap php$version-xml php$version-zip php$version-json php$version-bz2
php$version-pspell php$version-imagick php$version-pgsql php$version-imap php$version-ldap"
if [ -f /etc/redhat-release ]; then
# Packages in Remi repo have names with php$version-php- prefixes
mph="php$version-php-fpm php$version-php-cgi php$version-php-mysqlnd php$version-php-pgsql
php$version-php-pdo php$version-php-common php$version-php-pecl-imagick php$version-php-imap
php$version-php-ldap php$version-php-pecl-apcu php$version-php-pecl-zip php$version-php-cli
php$version-php-opcache php$version-php-xml php$version-php-gd php$version-php-intl
php$version-php-mbstring php$version-php-pspell php$version-php-readline"
if [ "$version" = "56" ]; then
mph=$(sed -e "s/php$version-php-pecl-apcu//")
fi
fi
# Check is version is 7.1 or below to add mcrypt
if [[ $(echo "$version 7.2" | awk '{print ($1 < $2)}') == 1 ]]; then
mph="$mph php$version-mcrypt"
fi
# Check if version is 8.0 or higher and drop php json.
if [[ ${version:0:1} == "8" ]]; then
mph=$(echo "$mph" | sed -e "s/php$version\(-php\)\{0,1\}-json//")
fi
if ! echo "$DB_SYSTEM" | grep -w 'mysql' > /dev/null; then
mph=$(echo "$mph" | sed -e "s/php$version\(-php\)\{0,1\}-mysql//")
fi
if ! echo "$DB_SYSTEM" | grep -w 'pgsql' > /dev/null; then
mph=$(echo "$mph" | sed -e "s/php$version\(-php\)\{0,1\}-pgsql//")
fi
# Install php packages
if [ -f '/etc/redhat-release' ]; then
dnf install -q -y $mph > /dev/null 2>&1 &
else
apt-get -qq update
apt-get -y -qq -o Dpkg::Options::="--force-confold" install $mph > /dev/null 2>&1 &
fi
BACK_PID=$!
# Check if package installation is done, print a spinner
echo "Installing PHP-$version, please wait..."
spinner="/-\|"
spin_i=1
while kill -0 $BACK_PID > /dev/null 2>&1; do
printf "\b${spinner:spin_i++%${#spinner}:1}"
sleep 0.5
done
# Do a blank echo to get the \n back
echo
# Check if installation was successful
if [ ! -f "$php_fpm" ]; then
echo "ERROR: Installation failed, please run the following command manually for debugging:"
if [ -f '/etc/redhat-release' ]; then
echo "dnf install $mph"
else
echo "apt-get install $mph"
fi
fi
# Check if required modules for apache2 are enabled
if [ "$WEB_SYSTEM" = "apache2" ]; then
if [ -f /etc/redhat-release ]; then
if ! httpd -M | grep 'proxy_fcgi_module' ; then
sed 's/#LoadModule proxy_fcgi_module/LoadModule proxy_fcgi_module/' -i /etc/httpd/conf.modules.d/00-proxy.conf
sed 's/#LoadModule proxy_module/LoadModule proxy_module/' -i /etc/httpd/conf.modules.d/00-proxy.conf
fi
if ! httpd -M | grep 'setenvif_module' ; then
sed 's/#LoadModule setenvif_module/LoadModule setenvif_module/' -i /etc/httpd/conf.modules.d/00-base.conf
fi
else
if ! a2query -q -m proxy_fcgi; then
a2enmod -q proxy_fcgi
fi
if ! a2query -q -m setenvif; then
a2enmod -q setenvif
fi
fi
$BIN/v-restart-web "yes"
fi
# Configure fpm
v_tpl=${version//./}
if [ -f /etc/redhat-release ]; then
systemctl enable php${version}-php-fpm
rm -f /etc/opt/remi/php${version}/php-fpm.d/*
cp -f $HESTIA_INSTALL_DIR/php-fpm/dummy.conf /etc/opt/remi/php${version}/php-fpm.d/
sed -i "s/9999/99$v_tpl/g" /etc/opt/remi/php${version}/php-fpm.d/dummy.conf
# Increase max upload and max post size
sed -i "s/upload_max_filesize = 2M/upload_max_filesize = 100M/g" /etc/opt/remi/php${version}/php.ini
sed -i "s/post_max_size = 8M/post_max_size = 100M/g" /etc/opt/remi/php${version}/php.ini
sed -i "s/max_execution_time = 30/max_execution_time = 60/g" /etc/opt/remi/php${version}/php.ini
else
update-rc.d php$version-fpm defaults > /dev/null 2>&1
rm -f /etc/php/$version/fpm/pool.d/*
cp -f $HESTIA_INSTALL_DIR/php-fpm/dummy.conf /etc/php/$version/fpm/pool.d/
sed -i "s/9999/99$v_tpl/g" /etc/php/$version/fpm/pool.d/dummy.conf
cp -f $HESTIA_INSTALL_DIR/php-fpm/php-fpm.conf /etc/php/$version/fpm/
sed -i "s/fpm_v/$version/g" /etc/php/$version/fpm/php-fpm.conf
# Increase max upload and max post size
sed -i "s/upload_max_filesize = 2M/upload_max_filesize = 100M/g" /etc/php/$version/fpm/php.ini
sed -i "s/post_max_size = 8M/post_max_size = 100M/g" /etc/php/$version/fpm/php.ini
sed -i "s/max_execution_time = 30/max_execution_time = 60/g" /etc/php/$version/fpm/php.ini
fi
# Install backend template
cp -f $HESTIA_INSTALL_DIR/php-fpm/multiphp.tpl \
$HESTIA/data/templates/web/php-fpm/PHP-${version/\./_}.tpl
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Info" "System" "Installed PHP $version."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,800 @@
#!/bin/bash
# info: backup system user with all its objects
# options: USER NOTIFY
#
# example: v-backup-user admin yes
#
# This function is used for backing up user with all its domains and databases.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
notify=${2-no}
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/db.sh
source $HESTIA/func/db.sh
# shellcheck source=/usr/local/hestia/func/backup.sh
source $HESTIA/func/backup.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Return Disk Usage
get_user_disk_usage() {
u_usage=0
web_exclusions=''
mail_exclusions=''
db_exclusions=''
user_exclusions=''
# Parsing excludes
if [ -e "$USER_DATA/backup-excludes.conf" ]; then
web_exclusions=$(grep 'WEB=' $USER_DATA/backup-excludes.conf \
| awk -F "WEB='" '{print $2}' | cut -f 1 -d \')
mail_exclusions=$(grep 'MAIL=' $USER_DATA/backup-excludes.conf \
| awk -F "MAIL='" '{print $2}' | cut -f 1 -d \')
db_exclusions=$(grep 'DB=' $USER_DATA/backup-excludes.conf \
| awk -F "DB='" '{print $2}' | cut -f 1 -d \')
user_exclusions=$(grep 'USER=' $USER_DATA/backup-excludes.conf \
| awk -F "USER='" '{print $2}' | cut -f 1 -d \')
fi
if [ -f "$USER_DATA/web.conf" ] && [ "$web_exclusions" != '*' ]; then
usage=0
domains=$(grep 'DOMAIN=' $USER_DATA/web.conf \
| awk -F "DOMAIN='" '{print $2}' | cut -f 1 -d \')
for domain in $domains; do
exclusion=$(echo -e "$web_exclusions" | tr ',' '\n' | grep "^$domain\|\*$")
if [ -z "$exclusion" ]; then
# Defining home directory
home_dir="$HOMEDIR/$user/web/$domain/"
exclusion=$(echo -e "$web_exclusions" | tr ',' '\n' | grep "^$domain\|\*:")
fargs=()
if [ -n "$exclusion" ]; then
xdirs=$(echo -e "$exclusion" | tr ':' '\n' | grep -v "$domain\|\*")
for xpath in $xdirs; do
fargs+=(--exclude="$xpath")
done
fi
# Checking home directory exist
if [ -e "$home_dir" ]; then
disk_usage=$(nice -n 19 du -shm "$home_dir" "${fargs[@]}" | cut -f 1)
u_usage=$((u_usage + disk_usage))
fi
fi
done
fi
if [ -f "$USER_DATA/mail.conf" ] && [ "$mail_exclusions" != '*' ]; then
usage=0
domains=$(grep 'DOMAIN=' "$USER_DATA/mail.conf" \
| awk -F "DOMAIN='" '{print $2}' | cut -f 1 -d \')
for domain in $domains; do
check_exl=$(echo "$mail_exclusions" | tr ',' '\n' | grep "^$domain$")
if [ -f "$USER_DATA/mail/$domain.conf" ] && [ -z "$check_exl" ]; then
accounts=$(grep 'ACCOUNT=' "$USER_DATA/mail/$domain.conf" \
| awk -F "ACCOUNT='" '{print $2}' | cut -f 1 -d \')
for account in $accounts; do
home_dir=$HOMEDIR/$user/mail/$domain/$account
exclusion=$(echo "$mail_exclusions" | tr ',' '\n' | grep "$domain:")
exclusion=$(echo "$exclusion" | tr ':' '\n' | grep -E "^$account|\*")
if [ -z "$exclusion" ] && [ -e "$home_dir" ]; then
disk_usage=$(nice -n 19 du -shm "$home_dir" | cut -f 1)
u_usage=$((u_usage + disk_usage))
fi
done
fi
done
fi
if [ -f "$USER_DATA/db.conf" ] && [ "$db_exclusions" != '*' ]; then
usage=0
databases=$(grep 'DB=' "$USER_DATA/db.conf" \
| awk -F "DB='" '{print $2}' | cut -f 1 -d \')
for database in $databases; do
exclusion=$(echo "$db_exclusions" | tr ',' '\n' | grep "^$database$")
if [ -z "$exclusion" ]; then
# Get database values
get_database_values
# Switching on db type
case $DB_SYSTEM in
mysql) get_mysql_disk_usage ;;
pgsql) get_pgsql_disk_usage ;;
esac
u_usage=$((u_usage + usage))
fi
done
fi
if [ "$user_exclusions" != '*' ]; then
fargs=()
for xpath in $(echo "$user_exclusions" | tr ',' '\n'); do
fargs+=(--exclude="$xpath")
done
usage=$(du -shm "$HOMEDIR/$user" --exclude "$HOMEDIR/$user/web" --exclude "$HOMEDIR/$user/mail" --exclude "$HOMEDIR/$user/conf" "${fargs[@]}" | cut -f 1)
u_usage=$((u_usage + usage))
fi
echo ${u_usage}
}
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '1' "$#" 'USER [NOTIFY]'
is_format_valid 'user'
is_system_enabled "$BACKUP_SYSTEM" 'BACKUP_SYSTEM'
is_object_valid 'user' 'USER' "$user"
if [ "$POLICY_BACKUP_SUSPENDED_USERS" != "yes" ]; then
is_object_unsuspended 'user' 'USER' "$user"
fi
is_backup_enabled
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
check_backup_conditions
# Set backup directory if undefined
if [ -z "$BACKUP" ]; then
BACKUP=/backup
fi
# Check if backup folder exists and have the correct permission
if [[ ! -d "$BACKUP" ]]; then
mkdir -p $BACKUP
fi
if [ "$(stat -c %a "$BACKUP")" != 755 ]; then
chmod 755 $BACKUP
fi
# Get current time
start_time=$(date '+%s')
# Set notification email and subject
subj="$user → backup failed"
email=$(grep CONTACT "$HESTIA/data/users/admin/user.conf" | cut -f 2 -d \')
# Validate available disk space (take usage * 2, due to the backup handling)
let u_disk=$(($(get_user_disk_usage) * 2))
let v_disk=$(($(stat -f --format="%a*%S" $BACKUP)))/1024/1024
if [ "$u_disk" -gt "$v_disk" ]; then
let u_disk_original=$(get_user_disk_usage)
# Always notify on failure
echo "Not enough disk space available ($v_disk mb) to perform the backup of $user. ( $u_disk_original mb * 2 = $u_disk mb). https://hestiacp.com/docs/server-administration/backup-restore.html" | $SENDMAIL -s "$subj" "$email" "yes"
# Deleting task from queue
sed -i "/v-backup-user $user /d" $HESTIA/data/queue/backup.pipe
check_result "$E_LIMIT" "not enough disk space available ($v_disk mb) to perform the backup of $user. ( $u_disk_original mb * 2 = $u_disk mb)."
fi
if [ -z "$BACKUP_TEMP" ]; then
BACKUP_TEMP=$BACKUP
fi
# Creating temporary directory
tmpdir=$(mktemp -p $BACKUP_TEMP -d)
if [ "$?" -ne 0 ]; then
echo "Can't create tmp dir $tmpdir" | $SENDMAIL -s "$subj" "$email" "yes"
# Deleting task from queue
sed -i "/v-backup-user $user /d" $HESTIA/data/queue/backup.pipe
check_result "$E_NOTEXIST" "can't create tmp dir"
fi
# Backup sys configs
echo "-- SYSTEM --" | tee $BACKUP/$user.log
mkdir $tmpdir/hestia
echo -e "$(date "+%F %T") $user.conf" | tee -a $BACKUP/$user.log
cp -r $USER_DATA/user.conf $tmpdir/hestia/
cp -r $USER_DATA/ssl $tmpdir/hestia/
if [ -e "$USER_DATA/stats.log" ]; then
echo -e "$(date "+%F %T") stats.log" | tee -a $BACKUP/$user.log
cp -r $USER_DATA/stats.log $tmpdir/hestia/
fi
if [ -e "$USER_DATA/history.log" ]; then
echo -e "$(date "+%F %T") history.log" | tee -a $BACKUP/$user.log
cp -r $USER_DATA/history.log $tmpdir/hestia/
fi
if [ -e "$USER_DATA/backup-excludes.conf" ]; then
echo -e "$(date "+%F %T") backup-excludes.conf" | tee -a $BACKUP/$user.log
cp -r $USER_DATA/backup-excludes.conf $tmpdir/hestia/
fi
# Backup PAM
mkdir $tmpdir/pam
echo -e "$(date "+%F %T") pam" | tee -a $BACKUP/$user.log
grep "^$user:" /etc/passwd > $tmpdir/pam/passwd
grep "^$user:" /etc/shadow > $tmpdir/pam/shadow
grep "^$user:" /etc/group > $tmpdir/pam/group
echo
# Parsing excludes
if [ -e "$USER_DATA/backup-excludes.conf" ]; then
source $USER_DATA/backup-excludes.conf
fi
# WEB domains
if [ -n "$WEB_SYSTEM" ] && [ "$WEB" != '*' ]; then
echo -e "\n-- WEB --" | tee -a $BACKUP/$user.log
mkdir $tmpdir/web/
# Parsing domain exclusions
conf="$USER_DATA/web.conf"
for domain in $(search_objects 'web' 'SUSPENDED' "*" 'DOMAIN'); do
exclusion=$(echo -e "$WEB" | tr ',' '\n' | grep "^$domain$\|^\*$")
if [ -z "$exclusion" ]; then
web_list="$web_list $domain"
else
echo "$(date "+%F %T") excluding $domain" | tee -a $BACKUP/$user.log
fi
done
web_list=$(echo "$web_list" | sed -e "s/ */\ /g" -e "s/^ //")
i=0
for domain in $web_list; do
check_backup_conditions
((i++))
echo -e "$(date "+%F %T") $domain" | tee -a $BACKUP/$user.log
mkdir -p $tmpdir/web/$domain/conf
mkdir -p $tmpdir/web/$domain/hestia
# Get domain variables
domain_idn=$domain
format_domain_idn
get_domain_values 'web'
# Backup web.conf
cd $tmpdir/web/$domain/
conf="$USER_DATA/web.conf"
grep "DOMAIN='$domain'" $conf > hestia/web.conf
# Backup vhost config
if [ -e "$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.conf" ]; then
cp $HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.conf* conf/
elif [ -e "$HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.conf" ]; then
cp $HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.conf* conf/
else
# legacy format: all domain configs in single file
tpl_file="$WEBTPL/$WEB_SYSTEM/$WEB_BACKEND/$TPL.tpl"
conf="$HOMEDIR/$user/conf/web/$WEB_SYSTEM.conf"
get_web_config_lines "$tpl_file" "$conf"
sed -n "$top_line,$bottom_line p" $conf > conf/$WEB_SYSTEM.conf
fi
# Backup ssl vhost
if [ "$SSL" = 'yes' ]; then
if [ -e "$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.ssl.conf" ]; then
cp $HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.ssl.conf* conf/
elif [ -e "$HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.ssl.conf" ]; then
cp $HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.ssl.conf* conf/
else
# legacy format: all domain configs in single file
tpl_file="$WEBTPL/$WEB_SYSTEM/$WEB_BACKEND/$TPL.stpl"
conf="$HOMEDIR/$user/conf/web/s$WEB_SYSTEM.conf"
get_web_config_lines "$tpl_file" "$conf"
sed -n "$top_line,$bottom_line p" $conf > conf/s$WEB_SYSTEM.conf
fi
fi
# Backup proxy config
if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then
if [ -e "$HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.conf" ]; then
cp $HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.conf* conf/
elif [ -e "$HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.conf" ]; then
cp $HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.conf* conf/
else
# legacy format: all domain configs in single file
tpl_file="$WEBTPL/$PROXY_SYSTEM/$PROXY.tpl"
conf="$HOMEDIR/$user/conf/web/$PROXY_SYSTEM.conf"
get_web_config_lines "$tpl_file" "$conf"
sed -n "$top_line,$bottom_line p" $conf > conf/$PROXY_SYSTEM.conf
fi
fi
# Backup ssl proxy config
if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ] && [ "$SSL" = 'yes' ]; then
if [ -e "$HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.ssl.conf" ]; then
cp $HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.ssl.conf* conf/
elif [ -e "$HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.ssl.conf" ]; then
cp $HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.ssl.conf* conf/
else
# legacy format: all domain configs in single file
tpl_file="$WEBTPL/$PROXY_SYSTEM/$PROXY.stpl"
conf="$HOMEDIR/$user/conf/web/s$PROXY_SYSTEM.conf"
get_web_config_lines "$tpl_file" "$conf"
sed -n "$top_line,$bottom_line p" $conf > conf/s$PROXY_SYSTEM.conf
fi
fi
domain_conf=$(grep "DOMAIN='$domain'" $conf)
parse_object_kv_list_non_eval domain_conf
mkdir -p template/$WEB_SYSTEM/
mkdir template/php-fpm/
if [ $WEB_BACKEND == 'php-fpm' ]; then
cp $HESTIA/data/templates/web/$WEB_SYSTEM/php-fpm/$TPL.tpl template/$WEB_SYSTEM/
cp $HESTIA/data/templates/web/$WEB_SYSTEM/php-fpm/$TPL.stpl template/$WEB_SYSTEM/
cp $HESTIA/data/templates/web/php-fpm/$BACKEND.tpl template/php-fpm/
else
cp $HESTIA/data/templates/web/$WEB_SYSTEM/$TPL.tpl template/$WEB_SYSTEM/
cp $HESTIA/data/templates/web/$WEB_SYSTEM/$TPL.stpl template/$WEB_SYSTEM/
fi
if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then
mkdir template/$PROXY_SYSTEM
cp $HESTIA/data/templates/web/$PROXY_SYSTEM/$PROXY.tpl template/$PROXY_SYSTEM/
cp $HESTIA/data/templates/web/$PROXY_SYSTEM/$PROXY.stpl template/$PROXY_SYSTEM/
fi
# Backup ssl certificates
if [ "$SSL" = 'yes' ]; then
cp $HOMEDIR/$user/conf/web/$domain/ssl/$domain.* conf/
cp $USER_DATA/ssl/$domain.* hestia/
fi
# Changin dir to documentroot
cd $HOMEDIR/$user/web/$domain
# Define exclude arguments
exclusion=$(echo -e "$WEB" | tr ',' '\n' | grep "^$domain\|\*:")
set -f
fargs=()
fargs+=(--exclude='./logs/*')
if [ -n "$exclusion" ]; then
if [[ "$exclusion" =~ '*' ]]; then
exclusion="${exclusion/\*/$domain}"
fi
xdirs="$(echo -e "$exclusion" | tr ':' '\n' | grep -v $domain)"
for xpath in $xdirs; do
if [ -d "$xpath" ]; then
fargs+=(--exclude=$xpath/*)
echo "$(date "+%F %T") excluding directory $xpath"
msg="$msg\n$(date "+%F %T") excluding directory $xpath"
else
echo "$(date "+%F %T") excluding file $xpath"
msg="$msg\n$(date "+%F %T") excluding file $xpath"
fargs+=(--exclude=$xpath)
fi
done
fi
set +f
# Backup files
if [ "$BACKUP_MODE" = 'zstd' ]; then
tar "${fargs[@]}" -cpf- * | pzstd -"$BACKUP_GZIP" - > $tmpdir/web/$domain/domain_data.tar.zst
else
tar "${fargs[@]}" -cpf- * | gzip -"$BACKUP_GZIP" - > $tmpdir/web/$domain/domain_data.tar.gz
fi
done
# Print total
if [ "$i" -eq 1 ]; then
echo -e "$(date "+%F %T") *** $i domain ***" | tee -a $BACKUP/$user.log
else
echo -e "$(date "+%F %T") *** $i domains ***" | tee -a $BACKUP/$user.log
fi
fi
# DNS domains
if [ -n "$DNS_SYSTEM" ] && [ "$DNS" != '*' ]; then
echo -e "\n-- DNS --" | tee -a $BACKUP/$user.log
mkdir $tmpdir/dns/
# Parsing domain exclusions
for domain in $(search_objects 'dns' 'SUSPENDED' "*" 'DOMAIN'); do
exclusion=$(echo "$DNS" | tr ',' '\n' | grep "^$domain$")
if [ -z "$exclusion" ]; then
dns_list="$dns_list $domain"
else
echo "$(date "+%F %T") excluding $domain"
msg="$msg\n$(date "+%F %T") excluding $domain"
fi
done
dns_list=$(echo "$dns_list" | sed -e "s/ */\ /g" -e "s/^ //")
i=0
for domain in $dns_list; do
((i++))
echo -e "$(date "+%F %T") $domain" | tee -a $BACKUP/$user.log
domain_idn="$domain"
# Building directory tree
mkdir -p $tmpdir/dns/$domain/conf
mkdir -p $tmpdir/dns/$domain/conf/keys
mkdir -p $tmpdir/dns/$domain/hestia
# Backup dns.conf
cd $tmpdir/dns/$domain/
conf="$USER_DATA/dns.conf"
grep "DOMAIN='$domain'" $conf > hestia/dns.conf
# Backup dns recods
cp $USER_DATA/dns/$domain.conf hestia/$domain.conf
if [ "$DNS_SYSTEM" != 'remote' ]; then
cp $HOMEDIR/$user/conf/dns/$domain.db conf/$domain.db
fi
# Backup DNSSEC public and private key if enabled
dnssec=$(grep "DOMAIN='$domain'" $USER_DATA/dns.conf | grep "DNSSEC='yes'")
if [ -n "$dnssec" ]; then
format_domain_idn
cp $USER_DATA/keys/K$domain_idn*.* $tmpdir/dns/$domain/conf/keys
fi
done
# Print total
if [ "$i" -eq 1 ]; then
echo -e "$(date "+%F %T") *** $i domain ***" | tee -a $BACKUP/$user.log
else
echo -e "$(date "+%F %T") *** $i domains ***" | tee -a $BACKUP/$user.log
fi
fi
# Mail domains
if [ -n "$MAIL_SYSTEM" ] && [ "$MAIL" != '*' ]; then
echo -e "\n-- MAIL --" | tee -a $BACKUP/$user.log
mkdir $tmpdir/mail/
# Parsing domain exclusions
conf="$USER_DATA/mail.conf"
for domain in $(search_objects 'mail' 'SUSPENDED' "*" 'DOMAIN'); do
check_exl=$(echo "$MAIL" | tr ',' '\n' | grep "^$domain$")
if [ -z "$check_exl" ]; then
mail_list="$mail_list $domain"
else
echo "$(date "+%F %T") excluding $domain" | tee -a $BACKUP/$user.log
fi
done
mail_list=$(echo "$mail_list" | sed -e "s/ */\ /g" -e "s/^ //")
i=0
for domain in $mail_list; do
check_backup_conditions
((i++))
echo -e "$(date "+%F %T") $domain" | tee -a $BACKUP/$user.log
mkdir -p $tmpdir/mail/$domain/conf
mkdir -p $tmpdir/mail/$domain/hestia
domain_idn=$domain
format_domain_idn
# Backup exim config
if [[ "$MAIL_SYSTEM" =~ exim ]]; then
cd $tmpdir/mail/$domain/
cp -r $HOMEDIR/$user/conf/mail/$domain/* conf/
fi
# Backup mail.conf
conf="$USER_DATA/mail.conf"
grep "DOMAIN='$domain'" $conf > hestia/mail.conf
cp $USER_DATA/mail/$domain.* hestia/
if [ -n "$(ls $USER_DATA/mail/ | grep *@$domain)" ]; then
cp $USER_DATA/mail/*@$domain.* hestia/
fi
# Backup emails
cd $HOMEDIR/$user/mail/$domain_idn
accounts=()
for account in *; do
exclusion=$(echo "$MAIL" | tr ',' '\n' | grep "$domain:")
exclusion=$(echo "$exclusion" | tr ':' '\n' | grep -E "^$account|\*")
# Checking exclusions
if [ -z "$exclusion" ] && [[ "$MAIL_SYSTEM" =~ exim ]]; then
accounts+=($account)
else
echo "$(date "+%F %T") excluding mail account $account" \
| tee -a $BACKUP/$user.log
fi
done
# Compress archive
if [ ${#accounts[@]} -gt 0 ]; then
if [ "$BACKUP_MODE" = 'zstd' ]; then
tar -cpf- "${accounts[@]}" | pzstd -"$BACKUP_GZIP" - > $tmpdir/mail/$domain/accounts.tar.zst
else
tar -cpf- "${accounts[@]}" | gzip -"$BACKUP_GZIP" - > $tmpdir/mail/$domain/accounts.tar.gz
fi
fi
done
# Print total
if [ "$i" -eq 1 ]; then
echo -e "$(date "+%F %T") *** $i domain ***" | tee -a $BACKUP/$user.log
else
echo -e "$(date "+%F %T") *** $i domains ***" | tee -a $BACKUP/$user.log
fi
fi
# Databases
if [ -n "$DB_SYSTEM" ] && [ "$DB" != '*' ]; then
echo -e "\n-- DB --" | tee -a $BACKUP/$user.log
mkdir $tmpdir/db/
# Parsing database exclusions
for database in $(search_objects 'db' 'SUSPENDED' "*" 'DB'); do
exclusion=$(echo "$DB" | tr ',' '\n' | grep "^$database$")
if [ -z "$exclusion" ]; then
db_list="$db_list $database"
else
echo "$(date "+%F %T") excluding $database" \
| tee -a $BACKUP/$user.log
fi
done
i=0
conf="$USER_DATA/db.conf"
db_list=$(echo "$db_list" | sed -e "s/ */\ /g" -e "s/^ //")
for database in $db_list; do
check_backup_conditions
((i++))
get_database_values
echo -e "$(date "+%F %T") $database ($TYPE)" | tee -a $BACKUP/$user.log
mkdir -p $tmpdir/db/$database/conf
mkdir -p $tmpdir/db/$database/hestia
cd $tmpdir/db/$database/
grep "DB='$database'" $conf > hestia/db.conf
dump="$tmpdir/db/$database/$database.$TYPE.sql"
if [ "$BACKUP_MODE" = 'zstd' ]; then
dumpgz="$tmpdir/db/$database/$database.$TYPE.sql.zst"
else
dumpgz="$tmpdir/db/$database/$database.$TYPE.sql.gz"
fi
grants="$tmpdir/db/$database/conf/$database.$TYPE.$DBUSER"
if [ ! -f "$dumpgz" ]; then
WAIT_LOOP_ENTERED=0
while true; do
if pgrep -x "mysqldump" > /dev/null; then
WAIT_LOOP_ENTERED=1
echo "Wait other mysqldump to finish"
sleep 1
else
if [ "$WAIT_LOOP_ENTERED" -eq 1 ]; then
echo "We can use mysqldump now"
fi
break
fi
done
case $TYPE in
mysql) dump_mysql_database ;;
pgsql) dump_pgsql_database ;;
esac
# Compress dump
if [ "$BACKUP_MODE" = 'zstd' ]; then
pzstd -$BACKUP_GZIP $dump
rm $dump
else
gzip -$BACKUP_GZIP $dump
fi
fi
done
# Print total
if [ "$i" -eq 1 ]; then
echo -e "$(date "+%F %T") *** $i database ***" \
| tee -a $BACKUP/$user.log
else
echo -e "$(date "+%F %T") *** $i databases ***" \
| tee -a $BACKUP/$user.log
fi
fi
# Cron jobs
if [ -n "$CRON_SYSTEM" ] && [ "$CRON" != '*' ]; then
echo -e "\n-- CRON --" | tee -a $BACKUP/$user.log
mkdir $tmpdir/cron/
# Backup cron.conf
cp $USER_DATA/cron.conf $tmpdir/cron/
cron_record=$(wc -l $USER_DATA/cron.conf | cut -f 1 -d ' ')
if [ -e "/var/spool/cron/$user" ]; then
cron_list="$cron_record"
cp /var/spool/cron/$user $tmpdir/cron/
fi
# Print total
if [ "$cron_record" -eq 1 ]; then
echo -e "$(date "+%F %T") *** $cron_record job ***" \
| tee -a $BACKUP/$user.log
else
echo -e "$(date "+%F %T") *** $cron_record jobs ***" \
| tee -a $BACKUP/$user.log
fi
fi
# User Directories
if [ "$USER" != '*' ]; then
echo -e "\n-- User Dir --" | tee -a $BACKUP/$user.log
mkdir $tmpdir/user_dir
cd $HOMEDIR/$user
# Parsing directory exclusions
USER=''
if [ -e "$USER_DATA/backup-excludes.conf" ]; then
source $USER_DATA/backup-excludes.conf
fi
fargs=()
for xpath in $(echo "$USER" | tr ',' '\n'); do
if [ -d "$xpath" ]; then
fargs+=(--exclude="$xpath"/*)
echo "$(date "+%F %T") excluding directory $xpath" \
| tee -a $BACKUP/$user.log
else
echo "$(date "+%F %T") excluding file $xpath" \
| tee -a $BACKUP/$user.log
fargs+=(--exclude="$xpath")
fi
done
IFS=$'\n'
set -f
i=0
for udir in $(ls -a | egrep -v "^conf$|^web$|^dns$|^tmp$|^mail$|^\.\.$|^\.$"); do
exclusion=$(echo "$USER" | tr ',' '\n' | grep "^$udir$")
if [ -z "$exclusion" ]; then
((i++))
udir_list="$udir_list $udir"
echo -e "$(date "+%F %T") adding $udir" | tee -a $BACKUP/$user.log
check_backup_conditions
# Backup files and dirs
if [ "$BACKUP_MODE" = 'zstd' ]; then
tar --anchored -cpf- "${fargs[@]}" $udir | pzstd -"$BACKUP_GZIP" - > $tmpdir/user_dir/$udir.tar.zst
else
tar --anchored -cpf- "${fargs[@]}" $udir | gzip -"$BACKUP_GZIP" - > $tmpdir/user_dir/$udir.tar.gz
fi
fi
done
set +f
udir_list=$(echo "$udir_list" | sed -e "s/ */\ /g" -e "s/^ //")
# Print total
if [ "$i" -eq 1 ]; then
echo -e "$(date "+%F %T") *** $i user directory ***" \
| tee -a $BACKUP/$user.log
else
echo -e "$(date "+%F %T") *** $i directories ***" \
| tee -a $BACKUP/$user.log
fi
fi
if [ "$BACKUP_MODE" = 'zstd' ]; then
touch $tmpdir/.zstd
fi
# Get backup size
size="$(du -shm $tmpdir | cut -f 1)"
# Get current time
end_time=$(date '+%s')
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
backup_new_date=$(date +"%Y-%m-%d_%H-%M-%S")
echo -e "\n-- SUMMARY --" | tee -a $BACKUP/$user.log
errorcode="0"
# Switching on backup system types
for backup_type in $(echo -e "${BACKUP_SYSTEM//,/\\n}"); do
case $backup_type in
local) local_backup ;;
ftp) ftp_backup ;;
sftp) sftp_backup ;;
b2) b2_backup ;;
rclone) rclone_backup ;;
esac
done
# Removing tmpdir
rm -rf $tmpdir
if [[ "$errorcode" != "0" ]]; then
if [[ "$BACKUP_SYSTEM" =~ "local" ]]; then
echo -e "$(date "+%F %T") *** Local backup was successfully executed. Remote backup failed ***" \
| tee -a $BACKUP/$user.log
BACKUP_SYSTEM="local"
else
echo -e "$(date "+%F %T") *** Remote backup failed ***" \
| tee -a $BACKUP/$user.log
exit $error_code
fi
fi
# Calculation run time
run_time=$((end_time - start_time))
run_time=$((run_time / 60))
current_time=$(date "+%T")
if [ "$run_time" -lt 1 ]; then
run_time=1
fi
min=minutes
if [ "$run_time" -eq 1 ]; then
min=minute
fi
echo "$(date "+%F %T") Size: $size MB" | tee -a $BACKUP/$user.log
echo "$(date "+%F %T") Runtime: $run_time $min" | tee -a $BACKUP/$user.log
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Removing duplicate
touch $USER_DATA/backup.conf
sed -i "/$user.$backup_new_date.tar/d" $USER_DATA/backup.conf
# Registering new backup
backup_str="BACKUP='$user.$backup_new_date.tar'"
backup_str="$backup_str TYPE='$BACKUP_SYSTEM' SIZE='$size'"
backup_str="$backup_str WEB='${web_list// /,}'"
backup_str="$backup_str DNS='${dns_list// /,}'"
backup_str="$backup_str MAIL='${mail_list// /,}'"
backup_str="$backup_str DB='${db_list// /,}'"
backup_str="$backup_str CRON='$cron_list'"
backup_str="$backup_str UDIR='${udir_list// /,}'"
backup_str="$backup_str RUNTIME='$run_time' TIME='$time' DATE='$date'"
echo "$backup_str" >> $USER_DATA/backup.conf
# Removing old backups
tail -n $BACKUPS $USER_DATA/backup.conf > $USER_DATA/backup.conf_
mv -f $USER_DATA/backup.conf_ $USER_DATA/backup.conf
chmod 660 $USER_DATA/backup.conf
chmod 660 $BACKUP/$user.log
# Deleting task from queue
sed -i "/v-backup-user $user /d" $HESTIA/data/queue/backup.pipe
U_BACKUPS=$(grep BACKUP $USER_DATA/backup.conf | wc -l)
update_user_value "$user" '$U_BACKUPS' "$U_BACKUPS"
cd $BACKUP
# Send notification
if [ -e "$BACKUP/$user.log" ] && [ "$notify" = "yes" ]; then
subj="$user → backup has been completed"
email=$(get_user_value '$CONTACT')
cat $BACKUP/$user.log | $SENDMAIL -s "$subj" "$email" "$notify"
$BIN/v-add-user-notification "$user" "Backup created successfully" "<p><span class='u-text-bold'>Archive:</span> <code>$user.$backup_new_date.tar</code></p>"
fi
# Logging
$BIN/v-log-action "$user" "Info" "Backup" "Backup created (Archive: $backup_new_date.tar)."
$BIN/v-log-action "system" "Info" "Backup" "Backup created (User: $user, Archive: $backup_new_date.tar)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,56 @@
#!/bin/bash
# info: backup all users
# options: NONE
#
# example: v-backup-users
#
# This function backups all system users.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Auto-repair all databases before backuping all accounts
mysqlrepair --all-databases --check --auto-repair > /dev/null 2>&1
if [ -z "$BACKUP_SYSTEM" ]; then
exit
fi
for user in $($BIN/v-list-sys-users plain); do
check_suspend=$(grep "SUSPENDED='no'" $HESTIA/data/users/$user/user.conf)
log=$HESTIA/log/backup.log
if [ ! -f "$HESTIA/data/users/$user/user.conf" ]; then
continue
fi
check_backup_conditions
check_suspend=$(grep "SUSPENDED='no'" $HESTIA/data/users/$user/user.conf)
log=$HESTIA/log/backup.log
if [ -n "$check_suspend" ]; then
echo -e "================================" >> $log
echo -e "$user" >> $log
echo -e "--------------------------------\n" >> $log
nice -n 19 ionice -c2 -n7 $BIN/v-backup-user $user >> $log 2>&1
echo -e "\n--------------------------------\n\n" >> $log
fi
done
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
exit

@ -0,0 +1,85 @@
#!/bin/bash
# info: change cron job
# options: USER JOB MIN HOUR DAY MONTH WDAY COMMAND
#
# example: v-change-cron-job admin 7 * * * * * * /usr/bin/uptime
#
# This function is used for changing existing job. It fully replace job
# parameters with new one but with same id.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
job=$2
min=$3
hour=$4
day=$5
month=$6
wday=$7
command=$8
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '7' "$#" 'USER JOB MIN HOUR DAY MONTH WDAY COMMAND'
is_format_valid 'user' 'job' 'min' 'hour' 'day' 'month' 'wday' 'command'
is_system_enabled "$CRON_SYSTEM" 'CRON_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'cron' 'JOB' "$job"
is_object_unsuspended 'cron' 'JOB' "$job"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Concatenating cron string
command=$(echo $command | sed -e "s/'/%quote%/g")
str="JOB='$job' MIN='$min' HOUR='$hour' DAY='$day' MONTH='$month' WDAY='$wday'"
str="$str CMD='$command' SUSPENDED='no' TIME='$time' DATE='$date'"
# Deleting old job
sed -i "/JOB='$job' /d" $USER_DATA/cron.conf
# Adding new
echo "$str" >> $USER_DATA/cron.conf
# Sorting jobs by id
sort_cron_jobs
# Sync system cron with user
sync_cron_jobs
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restarting crond
$BIN/v-restart-cron
check_result $? "Cron restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "Cron Jobs" "Cron job updated (Job: $job, Command: $command)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,78 @@
#!/bin/bash
# info: change database server password
# options: TYPE HOST USER PASSWORD
#
# example: v-change-database-host-password mysql localhost wp_user pA$$w@rD
#
# This function changes database server password.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
type=$1
host=$2
dbuser=$3
password=$4
HIDE=4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/db.sh
source $HESTIA/func/db.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
args_usage='TYPE HOST DBUSER DBPASS'
check_args '4' "$#" "$args_usage"
is_format_valid 'host' 'dbuser'
is_object_valid "../../conf/$type" 'HOST' "$host"
dbpass="$password"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Define email
email=$(grep CONTACT $HESTIA/data/users/admin/user.conf | cut -f2 -d \')
subj="v-change-database-host-password $*"
case $type in
mysql)
mysql_connect "$host"
query="USE mysql; UPDATE user SET"
query="$query password=PASSWORD('$dbpass')"
query="$query WHERE User='$dbuser';"
query="$query FLUSH PRIVILEGES;"
mysql_query "$query"
if [ "$dbuser" == "root" ]; then
echo -e "[client]\npassword='$dbpass'\n" > /root/.my.cnf
chmod 600 /root/.my.cnf
fi
;;
pgsql) echo "TBD" > /dev/null ;;
esac
update_object_value "../../conf/$type" 'HOST' "$host" '$USER' "$dbuser"
update_object_value "../../conf/$type" 'HOST' "$host" '$PASSWORD' "$dbpass"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Warning" "Database" "Password changed for remote database host (Host: $host)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,133 @@
#!/bin/bash
# info: change database owner
# options: DATABASE USER
#
# example: v-change-database-owner mydb alice
#
# This function for changing database owner.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
database=$1
user=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/db.sh
source $HESTIA/func/db.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'DATABASE USER'
is_format_valid 'database' 'user'
is_system_enabled "$DB_SYSTEM" 'DB_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
# Check owner existence
owner=$(echo $database | cut -f 1 -d '_')
if [ ! -d "$HESTIA/data/users/$owner" ]; then
echo "Error: database owner doesn't exist"
log_event "$E_NOTEXIST" "$ARGUMENTS"
exit "$E_NOTEXIST"
fi
# Check if owner is the same as the dst user
if [ "$owner" = "$user" ]; then
exit
fi
# Check db existence
db_data=$(grep "DB='$database'" $HESTIA/data/users/$owner/db.conf)
if [ -z "$db_data" ]; then
echo "Error: database $database doesn't exist"
log_event "$E_NOTEXIST" "$ARGUMENTS"
exit "$E_NOTEXIST"
fi
parse_object_kv_list "$db_data"
#Fix issue #1084 with "Upper case not allowed with PGSQL"
if [ "$TYPE" == "pgsql" ]; then
usersmall=$(echo "$user" | tr '[:upper:]' '[:lower:]')
else
usersmall=$user
fi
# Check if database name is uniqe
new_db=$(echo $database | sed "s/^${owner}_/${usersmall}_/")
check_db=$(grep "DB='$new_db'" $HESTIA/data/users/$user/db.conf)
if [ -n "$check_db" ]; then
echo "Error: $new_db database exists"
log_event "$E_EXISTS" "$ARGUMENTS"
exit "$E_EXISTS"
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Creating temporary directory
tmpdir=$(mktemp -p $BACKUP -d "tmp.$database.XXXXXXXXXX")
# Suspend database
$BIN/v-suspend-database $owner $database > /dev/null 2>&1
# Dump database
dump="$tmpdir/$database.$TYPE.sql"
grants="$tmpdir/$database.$TYPE.$DBUSER"
case $TYPE in
mysql) dump_mysql_database ;;
pgsql) dump_pgsql_database ;;
esac
# Import configuration
db_data=$(echo "$db_data" | sed "s/'${owner}_/'${usersmall}_/g")
echo "$db_data" >> $HESTIA/data/users/$user/db.conf
parse_object_kv_list "$db_data"
# Unsuspend db
$BIN/v-unsuspend-database "$user" "$new_db" > /dev/null 2>&1
# Rebuild databases
$BIN/v-rebuild-databases "$user"
# Import dump
case $TYPE in
mysql) import_mysql_database "$dump" ;;
pgsql) import_pgsql_database "$dump" ;;
esac
# Deleting tmpdir
rm -rf $tmpdir
# Remove old database
$BIN/v-unsuspend-database "$owner" "$database" > /dev/null 2>&1
$BIN/v-delete-database "$owner" "$database" > /dev/null 2>&1
# Update counters
$BIN/v-update-user-counters "$owner"
$BIN/v-update-user-counters "$user"
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,69 @@
#!/bin/bash
# info: change database password
# options: USER DATABASE DBPASS
#
# example: v-change-database-password admin wp_db neW_pAssWorD
#
# This function for changing database user password to a database. It uses the
# full name of database as argument.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
database=$2
password=$3
HIDE=3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/db.sh
source $HESTIA/func/db.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DATABASE DBPASS'
is_format_valid 'user' 'database'
is_system_enabled "$DB_SYSTEM" 'DB_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'db' 'DB' "$database"
is_object_unsuspended 'db' 'DB' "$database"
is_password_valid
dbpass="$password"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Get database values
get_database_values
case $TYPE in
mysql) change_mysql_password ;;
pgsql) change_pgsql_password ;;
esac
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Update config value
update_object_value 'db' 'DB' "$database" '$MD5' "$md5"
# Logging
$BIN/v-log-action "$user" "Info" "Database" "Database password changed (Database: $database)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,109 @@
#!/bin/bash
# info: change database username
# options: USER DATABASE DBUSER [DBPASS]
#
# example: v-change-database-user admin my_db joe_user
#
# This function for changing database user. It uses the
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
database=$2
dbuser="$user"_"$3"
password=$4
HIDE=4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/db.sh
source $HESTIA/func/db.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DATABASE DBUSER [DBPASS]'
is_format_valid 'user' 'database' 'dbuser'
is_system_enabled "$DB_SYSTEM" 'DB_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'db' 'DB' "$database"
is_object_new 'db' 'DBUSER' "$dbuser"
is_object_unsuspended 'db' 'DB' "$database"
is_password_valid
dbpass="$password"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Compare old and new user
old_dbuser=$(get_object_value 'db' 'DB' "$database" '$DBUSER')
if [ "$old_dbuser" = "$dbuser" ]; then
exit
fi
# Set new dbuser
update_object_value 'db' 'DB' "$database" '$DBUSER' "$dbuser"
# Get database values
get_database_values
#Fix issue #1084 with "Upper case not allowed with PGSQL"
if [ "$TYPE" = "pgsql" ]; then
dbuser=$(echo $dbuser | tr '[:upper:]' '[:lower:]')
exclude="-"
if [[ "$dbuser" =~ $exclude ]]; then
check_result "$E_INVALID" "invalid database user format"
fi
fi
# Rebuild database
case $TYPE in
mysql) rebuild_mysql_database ;;
pgsql) rebuild_pgsql_database ;;
esac
# Change password
if [ -n "$dbpass" ]; then
case $TYPE in
mysql) change_mysql_password ;;
pgsql) change_pgsql_password ;;
esac
# Update config value
update_object_value 'db' 'DB' "$database" '$MD5' "$md5"
fi
# Remove old user
check_old_dbuser=$(grep "DBUSER='$old_dbuser'" $USER_DATA/db.conf)
if [ -z "$check_old_dbuser" ]; then
case $TYPE in
mysql) delete_mysql_user ;;
pgsql) delete_pgsql_user ;;
esac
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "$user" "Info" "Database" "Database user changed (Database: $database, User: $dbuser)"
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,107 @@
#!/bin/bash
# info: change dns domain dnssec status
# options: USER DOMAIN STATUS
#
# example: v-change-dns-domain-dnssec admin domain.pp.ua yes
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
status=$3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# shellcheck source=/usr/local/hestia/func/syshealth.sh
source $HESTIA/func/syshealth.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN STATUS'
is_format_valid 'user' 'domain' ''
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_object_unsuspended 'dns' 'DOMAIN' "$domain"
if [ -n "$status" ]; then
is_boolean_format_valid "$status" 'status'
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
if [ "$DNS_CLUSTER_SYSTEM" != 'hestia-zone' ]; then
check_result "$E_DISABLED" "DNSSEC is not supported when DNS_CLUSTER_SYSTEM is not set to hestia-zone"
fi
version=$(named -v | awk 'NR==1{print $2}')
if version_ge '9.16.18' $version; then
check_result "$E_DISABLED" "DNSSEC is not supported when bind / named version <= 9.16.18"
fi
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
if [ -f "$HESTIA/data/queue/dns-cluster.pipe" ]; then
bash $HESTIA/data/queue/dns-cluster.pipe
fi
syshealth_repair_dns_config
# Changing exp
update_object_value 'dns' 'DOMAIN' "$domain" '$DNSSEC' "$status"
# Update serial
update_domain_serial
# Rebuild DNS config
rebuild_dns_domain_conf
if [ $status = "no" ]; then
update_object_value 'dns' 'DOMAIN' "$domain" '$KEY' ""
# Delete existing keys
rm -fr $HOMEDIR/$user/conf/dns/$domain.db.*
rm -fr /var/cache/bind/K$domain_idn.*
rm -fr $USER_DATA/keys/K$domain_idn.*
fi
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
# Check for first sync
dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe)
if [ -z "$dlock" ]; then
cmd="$BIN/v-add-remote-dns-domain $user $domain yes"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "$user" "Info" "DNS" "Updated DNS SOA expiration date (Domain: $domain, Value: $exp)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,73 @@
#!/bin/bash
# info: change dns domain expiration date
# options: USER DOMAIN EXP
#
# example: v-change-dns-domain-exp admin domain.pp.ua 2020-11-20
#
# This function of changing the term of expiration domain's registration. The
# serial number will be refreshed automatically during update.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
exp=$3
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN EXP'
is_format_valid 'user' 'domain' 'exp'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_object_unsuspended 'dns' 'DOMAIN' "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Changing exp
update_object_value 'dns' 'DOMAIN' "$domain" '$EXP' "$exp"
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
# Check for first sync
dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe)
if [ -z "$dlock" ]; then
cmd="$BIN/v-change-remote-dns-domain-exp $user $domain $exp"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "$user" "Info" "DNS" "Updated DNS SOA expiration date (Domain: $domain, Value: $exp)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,95 @@
#!/bin/bash
# info: change dns domain ip address
# options: USER DOMAIN IP [RESTART]
#
# example: v-change-dns-domain-ip admin domain.com 123.212.111.222
#
# This function for changing the main ip of DNS zone.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
ip=$3
restart=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN IP [RESTART]'
is_format_valid 'user' 'domain' 'ip'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_object_unsuspended 'dns' 'DOMAIN' "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Get old ip
get_domain_values 'dns'
old=$IP
# Changing ip
update_object_value 'dns' 'DOMAIN' "$domain" '$IP' "$ip"
# Changing records
sed -i "s/$old/$ip/g" $USER_DATA/dns/$domain.conf
# Update serial
update_domain_serial
# Updating zone
if [[ "$DNS_SYSTEM" =~ named|bind ]]; then
rebuild_dns_domain_conf
fi
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
# Check for first sync
dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe)
if [ -z "$dlock" ]; then
cmd="$BIN/v-add-remote-dns-domain $user $domain yes"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restarting named
$BIN/v-restart-dns "$restart"
check_result $? "DNS restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "DNS" "IP address for DNS domain changed (IP: $ip, Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,89 @@
#!/bin/bash
# info: change dns domain soa record
# options: USER DOMAIN SOA [RESTART]
#
# example: v-change-dns-domain-soa admin acme.com d.ns.domain.tld
#
# This function for changing SOA record. This type of records can not be
# modified by v-change-dns-record call.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
soa=$(echo $3 | sed -e 's/\.*$//g' -e 's/^\.*//g')
restart=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN SOA [RESTART]'
is_format_valid 'user' 'domain' 'soa'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_object_unsuspended 'dns' 'DOMAIN' "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Changing soa
update_object_value 'dns' 'DOMAIN' "$domain" '$SOA' "$soa"
# Update serial
update_domain_serial
# Updating zone
if [[ "$DNS_SYSTEM" =~ named|bind ]]; then
rebuild_dns_domain_conf
fi
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
# Check for first sync
dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe)
if [ -z "$dlock" ]; then
cmd="$BIN/v-change-remote-dns-domain-soa $user $domain $soa"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restarting named
$BIN/v-restart-dns "$restart"
check_result $? "DNS restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "DNS" "SOA record for DNS domain changed (SOA: $soa, Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,172 @@
#!/bin/bash
# info: change dns domain template
# options: USER DOMAIN TEMPLATE [RESTART]
#
# example: v-change-dns-domain-tpl admin example.com child-ns yes
#
# This function for changing the template of records. By updating old records
# will be removed and new records will be generated in accordance with
# parameters of new template.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
template=$3
restart=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN TEMPLATE [RESTART]'
is_format_valid 'user' 'domain' 'template'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_object_unsuspended 'dns' 'DOMAIN' "$domain"
is_dns_template_valid "$template"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Defining variables
get_domain_values 'dns'
i=1
ns=$(get_user_value '$NS')
for nameserver in ${ns//,/ }; do
eval ns$i=$nameserver
((++i))
done
# Reading template
template_data=$(cat "$DNSTPL/$template.tpl")
# Deleting unused nameservers
if [ -z "$ns3" ]; then
template_data=$(echo "$template_data" | grep -v %ns3%)
fi
if [ -z "$ns4" ]; then
template_data=$(echo "$template_data" | grep -v %ns4%)
fi
if [ -z "$ns5" ]; then
template_data=$(echo "$template_data" | grep -v %ns5%)
fi
if [ -z "$ns6" ]; then
template_data=$(echo "$template_data" | grep -v %ns6%)
fi
if [ -z "$ns7" ]; then
template_data=$(echo "$template_data" | grep -v %ns7%)
fi
if [ -z "$ns8" ]; then
template_data=$(echo "$template_data" | grep -v %ns8%)
fi
# Changing tpl
echo "$template_data" \
| sed -e "s/%ip%/$IP/g" \
-e "s/%domain_idn%/$domain_idn/g" \
-e "s/%domain%/$domain/g" \
-e "s/%ns1%/$ns1/g" \
-e "s/%ns2%/$ns2/g" \
-e "s/%ns3%/$ns3/g" \
-e "s/%ns4%/$ns4/g" \
-e "s/%ns5%/$ns5/g" \
-e "s/%ns6%/$ns6/g" \
-e "s/%ns7%/$ns7/g" \
-e "s/%ns8%/$ns8/g" \
-e "s/%time%/$TIME/g" \
-e "s/%date%/$DATE/g" > $USER_DATA/dns/$domain.conf
records="$(wc -l $USER_DATA/dns/$domain.conf | cut -f 1 -d ' ')"
# Refresh DKIM records in DNS if signing key exists for domain
if [ "$template" = "default" ] || [ "$template" = "child-ns" ]; then
if [ -n "$MAIL_SYSTEM" ] && [ -f "$HOMEDIR/$user/conf/mail/$domain/dkim.pem" ]; then
check_dns_domain=$(is_object_valid 'dns' 'DOMAIN' "$domain")
if [ "$?" -eq 0 ]; then
p=$(cat "$USER_DATA/mail/$domain.pub" | grep -v ' KEY---' | tr -d '\n')
record='_domainkey'
policy="\"t=y; o=~;\""
$BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$policy" '' '' 'no'
record='mail._domainkey'
selector="\"v=DKIM1\; k=rsa\; p=$p\""
$BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$selector"
fi
fi
fi
# Set correct domain name formatting for Office 365/Microsoft 365 MX records
if [ "$template" = "office365" ]; then
check_dns_domain=$(is_object_valid 'dns' 'DOMAIN' "$domain")
if [ "$?" -eq 0 ]; then
record='@'
formatted_domain=$(echo "$domain" | sed 's/\./-/g')
$BIN/v-add-dns-record "$user" "$domain" "$record" MX "${formatted_domain}.mail.protection.outlook.com." '0' '' "$restart"
fi
fi
# Update serial
update_domain_serial
# Updating zone
if [[ "$DNS_SYSTEM" =~ named|bind ]]; then
rebuild_dns_domain_conf
fi
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
# Check for first sync
dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe)
if [ -z "$dlock" ]; then
cmd="$BIN/v-add-remote-dns-domain $user $domain domain"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Updating hestia config
update_object_value 'dns' 'DOMAIN' "$domain" '$TPL' "$template"
update_object_value 'dns' 'DOMAIN' "$domain" '$RECORDS' "$records"
# Rebuild DNS zone and update counters to ensure that it has updated correctly
$BIN/v-rebuild-dns-domain "$user" "$domain" no
$BIN/v-update-user-stats "$user"
$BIN/v-update-user-counters "$user"
# Restarting named
$BIN/v-restart-dns "$restart"
check_result $? "DNS restart failed" > /dev/null
# Logging
$BIN/v-log-action "system" "Info" "DNS" "Template for DNS domain changed (Template: $template, Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,88 @@
#!/bin/bash
# info: change dns domain ttl
# options: USER DOMAIN TTL [RESTART]
#
# example: v-change-dns-domain-ttl alice example.com 14400
#
# This function for changing the time to live TTL parameter for all records.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
ttl=$3
restart=$4
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '3' "$#" 'USER DOMAIN TTL [RESTART]'
is_format_valid 'user' 'domain' 'ttl'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_object_unsuspended 'dns' 'DOMAIN' "$domain"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Changing ttl
update_object_value 'dns' 'DOMAIN' "$domain" '$TTL' "$ttl"
# Update serial
update_domain_serial
# Updating zone
if [[ "$DNS_SYSTEM" =~ named|bind ]]; then
rebuild_dns_domain_conf
fi
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
# Check for first sync
dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe)
if [ -z "$dlock" ]; then
cmd="$BIN/v-change-remote-dns-domain-ttl $user $domain $ttl"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restarting named
$BIN/v-restart-dns "$restart"
check_result $? "DNS restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "DNS" "TTL for DNS domain changed (TTL: $ttl, Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,168 @@
#!/bin/bash
# info: change dns domain record
# options: USER DOMAIN ID RECORD TYPE VALUE [PRIORITY] [RESTART] [TTL]
#
# example: v-change-dns-record admin domain.ua 42 192.18.22.43
#
# This function for changing DNS record.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
id=$3
record=$4
rtype=$5
dvalue=$6
priority=$7
restart=$8
ttl=$9
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '6' "$#" 'USER DOMAIN ID RECORD TYPE VALUE [PRIORITY] [RESTART] [TTL]'
is_format_valid 'user' 'domain' 'id' 'record'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_object_unsuspended 'dns' 'DOMAIN' "$domain"
is_object_valid "dns/$domain" 'ID' "$id"
is_format_valid 'ttl'
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
if [ "$record" = "@" ] && [ "$rtype" = "CNAME" ]; then
check_result $E_INVALID "CNAME on root is not allowed"
fi
# Make sure the variable for the optional parameter TTL is empty before parsing
# We depend on that later on
unset TTL
# Parsing domain config
line=$(grep "ID='$id'" $USER_DATA/dns/$domain.conf)
parse_object_kv_list "$line"
if [ -z "$rtype" ]; then
rtype=$TYPE
fi
if [ -z "$priority" ]; then
priority=$PRIORITY
fi
# Null priority for none MX/SRV records
if [ "$rtype" != 'MX' ] && [ "$rtype" != 'SRV' ]; then
priority=''
fi
# Add trailing dot at the end of NS/CNAME/MX/PTR/SRV record
if [[ $rtype =~ ^NS|CNAME|MX|PTR|SRV ]]; then
trailing_dot=$(echo "$dvalue" | grep "\.$")
if [ -z "$trailing_dot" ]; then
dvalue="$dvalue."
fi
fi
if [[ $rtype =~ NS|CNAME|MX|PTR|SRV ]]; then
dvalue=$(idn2 --quiet "$dvalue")
record=$(idn2 --quiet "$record")
fi
if [ "$rtype" != "CAA" ]; then
dvalue=${dvalue//\"/}
if [ "$rtype" != 'SRV' ] && [[ "$dvalue" =~ [\;[:space:]] ]]; then
dvalue='"'"$dvalue"'"'
fi
fi
#RTYPE wasn't checked make sure to do it now correctly
is_format_valid 'user' 'domain' 'id' 'record' 'rtype' 'dvalue'
# Additional verifications
is_dns_fqnd "$TYPE" "$dvalue"
is_dns_nameserver_valid "$domain" "$TYPE" "$dvalue"
if [[ "$RECORD" == "$record" ]] && [[ "$TYPE" == "$rtype" ]] && [[ "$PRIORITY" -eq "$priority" ]] \
&& [[ "$VALUE" == "$dvalue" ]] && [[ "$SUSPENDED" == 'no' ]] && [[ "$TTL" -eq "$ttl" ]]; then
echo "No pending changes in DNS entry."
exit "$E_EXISTS"
fi
# Generating timestamp
time_n_date=$(date +'%T %F')
time=$(echo "$time_n_date" | cut -f 1 -d \ )
date=$(echo "$time_n_date" | cut -f 2 -d \ )
# Adding record
dns_rec="ID='$id' RECORD='$record' TYPE='$rtype' PRIORITY='$priority'"
dns_rec="$dns_rec VALUE='$dvalue' SUSPENDED='no' TIME='$time' DATE='$date'"
[ -n "$ttl" ] && dns_rec="$dns_rec TTL='$ttl'"
# Deleting old record
sed -i "/^ID='$id'/d" $USER_DATA/dns/$domain.conf
# Add new entry
echo "$dns_rec" >> $USER_DATA/dns/$domain.conf
# Sorting records
sort_dns_records
# Update serial
update_domain_serial
# Updating zone
if [[ "$DNS_SYSTEM" =~ named|bind ]]; then
rebuild_dns_domain_conf
fi
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
# Check for first sync
dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe)
if [ -z "$dlock" ]; then
cmd="$BIN/v-add-remote-dns-domain $user $domain records"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restarting named
$BIN/v-restart-dns "$restart"
check_result $? "DNS restart failed" > /dev/null
# Logging
$BIN/v-log-action "$user" "Info" "DNS" "DNS record value changed (Type: $rtype, Record: $record, Value: $dvalue, Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,93 @@
#!/bin/bash
# info: change dns domain record id
# options: USER DOMAIN ID NEWID [RESTART]
#
# example: v-change-dns-record-id admin acme.com 24 42 yes
#
# This function for changing internal record id.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
user=$1
domain=$2
domain_idn=$2
id=$3
newid=$4
restart=$5
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/domain.sh
source $HESTIA/func/domain.sh
# shellcheck source=/usr/local/hestia/func/rebuild.sh
source $HESTIA/func/rebuild.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
# Additional argument formatting
format_domain
format_domain_idn
# TODO: $domain_idn not used in this script - maybe $domain should be converted to $doman_idn ?
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '4' "$#" 'USER DOMAIN ID NEWID [RESTART]'
is_format_valid 'user' 'domain' 'id' 'newid'
is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
is_object_valid 'dns' 'DOMAIN' "$domain"
is_object_unsuspended 'dns' 'DOMAIN' "$domain"
is_object_valid "dns/$domain" 'ID' "$id"
is_object_new "dns/$domain" 'ID' "$newid"
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
# Change id
sed -i "s/^ID='$id'/ID='$newid'/" $USER_DATA/dns/$domain.conf
# Sorting records
sort_dns_records
# Update serial
update_domain_serial
# Updating zone
if [[ "$DNS_SYSTEM" =~ named|bind ]]; then
rebuild_dns_domain_conf
fi
# Updating dns-cluster queue
if [ "$DNS_CLUSTER" = "yes" ]; then
# Check for first sync
dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe)
if [ -z "$dlock" ]; then
cmd="$BIN/v-add-remote-dns-domain $user $domain records"
echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe
fi
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Restarting named
$BIN/v-restart-dns "$restart"
check_result $? "DNS restart failed" > /dev/null
# Logging
log_event "$OK" "$ARGUMENTS"
exit

@ -0,0 +1,317 @@
#!/bin/bash
# info: change domain owner
# options: DOMAIN USER
#
# example: v-change-domain-owner www.example.com bob
#
# This function of changing domain ownership.
#----------------------------------------------------------#
# Variables & Functions #
#----------------------------------------------------------#
# Argument definition
domain=$1
user=$2
# Includes
# shellcheck source=/etc/hestiacp/hestia.conf
source /etc/hestiacp/hestia.conf
# shellcheck source=/usr/local/hestia/func/main.sh
source $HESTIA/func/main.sh
# shellcheck source=/usr/local/hestia/func/ip.sh
source $HESTIA/func/ip.sh
# load config file
source_conf "$HESTIA/conf/hestia.conf"
#----------------------------------------------------------#
# Verifications #
#----------------------------------------------------------#
check_args '2' "$#" 'DOMAIN USER'
is_format_valid 'domain' 'user'
is_object_valid 'user' 'USER' "$user"
is_object_unsuspended 'user' 'USER' "$user"
owner=$($BIN/v-search-domain-owner "$domain")
if [ -z "$owner" ]; then
check_result "$E_NOTEXIST" "domain $domain doesn't exist"
fi
if [ "$owner" = "$user" ]; then
echo "ERROR: $domain is already owned by $user."
exit
fi
# Perform verification if read-only mode is enabled
check_hestia_demo_mode
#----------------------------------------------------------#
# Action #
#----------------------------------------------------------#
echo "Moving $domain from $owner to $user, please wait..."
# WEB domain
web_data=$(grep "DOMAIN='$domain'" $HESTIA/data/users/$owner/web.conf)
if [ -n "$web_data" ]; then
echo "[*] Moving web domain..."
$BIN/v-suspend-web-domain "$owner" "$domain" >> /dev/null 2>&1
parse_object_kv_list "$web_data"
if [ -n "$CUSTOM_DOCROOT" ]; then
if [ -z $(echo $CUSTOM_DOCROOT | grep -i "/home/$owner/web/$domain") ]; then
docdomain=$(echo "$CUSTOM_DOCROOT" | cut -d'/' -f5)
$BIN/v-list-web-domain $user $docdomain > /dev/null
if [ "$?" != 0 ]; then
echo "Error: Target domain doesn't belong to the new user"
exit $E_NOTEXIST
fi
new_docroot=$(echo $CUSTOM_DOCROOT | sed "s/\/home\/$owner/\/home\/$user/")
new_php_docroot=$(echo $CUSTOM_PHPROOT | sed "s/\/home\/$owner/\/home\/$user/")
else
new_docroot=$(echo $CUSTOM_DOCROOT | sed "s/\/home\/$owner/\/home\/$user/")
new_php_docroot=$(echo $CUSTOM_PHPROOT | sed "s/\/home\/$owner/\/home\/$user/")
fi
fi
if [ -z "$(echo $(get_user_ips) | grep $IP)" ]; then
echo "[*] IP dedicated to $owner select new ip address..."
get_user_ip
fi
old_ip=$IP
new_ip=$ip
echo $ip
# Change IP
if [ -n "$ip" ]; then
web_data=$(echo "$web_data" | sed "s/IP='$IP'/IP='$ip'/")
fi
if [ -n "$new_docroot" ]; then
web_data=$(echo "$web_data" | sed "s|CUSTOM_DOCROOT='$CUSTOM_DOCROOT'|CUSTOM_DOCROOT='$new_docroot'|")
web_data=$(echo "$web_data" | sed "s|CUSTOM_PHPROOT='$CUSTOM_PHPROOT'|CUSTOM_PHPROOT='$new_php_docroot'|")
fi
web_data=$(echo "$web_data" | sed "s/SUSPENDED='no'/SUSPENDED='yes'/")
# Check SSL
if [ "$SSL" = 'yes' ]; then
ssl_crt=$HESTIA/data/users/$owner/ssl/$domain.crt
ssl_key=$HESTIA/data/users/$owner/ssl/$domain.key
ssl_ca=$HESTIA/data/users/$owner/ssl/$domain.ca
ssl_pem=$HESTIA/data/users/$owner/ssl/$domain.pem
mv $ssl_crt $HESTIA/data/users/$user/ssl/
mv $ssl_key $HESTIA/data/users/$user/ssl/
mv $ssl_ca $HESTIA/data/users/$user/ssl/ >> /dev/null 2>&1
mv $ssl_pem $HESTIA/data/users/$user/ssl/ >> /dev/null 2>&1
rm -f $HOMEDIR/$owner/conf/web/ssl.$domain.*
fi
# Check ftp user account
if [ -n "$FTP_USER" ]; then
/usr/sbin/userdel -f "$FTP_USER" >> /dev/null 2>&1
old_str="FTP_USER='$FTP_USER'"
new_str=$(echo "$old_str" | sed "s/${owner}_/${user}_/")
web_data=$(echo "$web_data" | sed "s/$old_str/$new_str/")
fi
# Move config
sed -i "/DOMAIN='$domain'/d" $HESTIA/data/users/$owner/web.conf
echo "$web_data" >> $HESTIA/data/users/$user/web.conf
# Move data
mv $HOMEDIR/$owner/web/$domain $HOMEDIR/$user/web/
# Change ownership
find "$HOMEDIR/$user/web/$domain" -user "$owner" \
-exec chown -h $user:$user {} \;
# Rebuild config
$BIN/v-unsuspend-web-domain "$user" "$domain" no >> /dev/null 2>&1
$BIN/v-rebuild-web-domains "$owner" no
$BIN/v-rebuild-web-domains "$user"
# Cleanup old config
rm -rf "$HOMEDIR/$owner/conf/web/$domain/"
fi
# DNS domain
dns_data=$(grep "DOMAIN='$domain'" $HESTIA/data/users/$owner/dns.conf)
if [ -n "$dns_data" ]; then
echo "[*] Moving DNS zone and records..."
parse_object_kv_list "$dns_data"
# Change IP
if [ -n "$ip" ]; then
dns_data=$(echo "$dns_data" | sed "s/IP='$IP'/IP='$ip'/")
sed -i "s/$IP/$ip/g" $HESTIA/data/users/$owner/dns/$domain.conf
fi
# Move config
sed -i "/DOMAIN='$domain'/d" $HESTIA/data/users/$owner/dns.conf
echo "$dns_data" >> $HESTIA/data/users/$user/dns.conf
# Move dns records
mv $HESTIA/data/users/$owner/dns/$domain.conf \
$HESTIA/data/users/$user/dns/
# Rebuild config
$BIN/v-unsuspend-dns-domain "$user" "$domain" no >> /dev/null 2>&1
$BIN/v-rebuild-dns-domains "$owner" no
$BIN/v-rebuild-dns-domains "$user"
# Cleanup old config
rm -f $HOMEDIR/$owner/conf/dns/$domain.db
# Resync dns cluster
if [ "$DNS_CLUSTER" = "yes" ]; then
$BIN/v-sync-dns-cluster
fi
fi
# MAIL domain
mail_data=$(grep "DOMAIN='$domain'" $HESTIA/data/users/$owner/mail.conf)
if [ -n "$mail_data" ]; then
$BIN/v-suspend-mail-domain "$owner" "$domain" >> /dev/null 2>&1
echo "[*] Moving mail domain and accounts..."
parse_object_kv_list "$mail_data"
# Ensure mail configuration directory exists for receiving user
if [ ! -e "$HOMEDIR/$user/conf/mail/$domain/" ]; then
mkdir -p $HOMEDIR/$user/conf/mail/$domain/
fi
# Move config
sed -i "/DOMAIN='$domain'/d" $HESTIA/data/users/$owner/mail.conf
mail_data=$(echo "$mail_data" | sed "s/SUSPENDED='no'/SUSPENDED='yes'/")
echo "$mail_data" >> $HESTIA/data/users/$user/mail.conf
mv -f $HESTIA/data/users/$owner/mail/$domain.conf \
$HESTIA/data/users/$user/mail/
# Move DKIM
if [ -e "$HESTIA/data/users/$owner/mail/$domain.pem" ]; then
mv -f $HESTIA/data/users/$owner/mail/$domain.pem \
$HESTIA/data/users/$user/mail/
mv -f $HESTIA/data/users/$owner/mail/$domain.pub \
$HESTIA/data/users/$user/mail/
fi
# Move SSL certificates
if [ "$SSL" = 'yes' ]; then
# Ensure that SSL directory exists and move certificates
mkdir -p $HESTIA/data/users/$user/ssl/
mkdir -p $HOMEDIR/$user/conf/mail/$domain/ssl/
ssl_crt=$HESTIA/data/users/$owner/ssl/mail.$domain.crt
ssl_key=$HESTIA/data/users/$owner/ssl/mail.$domain.key
ssl_ca=$HESTIA/data/users/$owner/ssl/mail.$domain.ca
ssl_pem=$HESTIA/data/users/$owner/ssl/mail.$domain.pem
mv $ssl_crt $HESTIA/data/users/$user/ssl/
mv $ssl_key $HESTIA/data/users/$user/ssl/
mv $ssl_ca $HESTIA/data/users/$user/ssl/ >> /dev/null 2>&1
mv $ssl_pem $HESTIA/data/users/$user/ssl/ >> /dev/null 2>&1
# Add certificate to user home directory
cp -f $HESTIA/data/users/$user/ssl/mail.$domain.crt $HOMEDIR/$user/conf/mail/$domain/ssl/$domain.crt
cp -f $HESTIA/data/users/$user/ssl/mail.$domain.key $HOMEDIR/$user/conf/mail/$domain/ssl/$domain.key
cp -f $HESTIA/data/users/$user/ssl/mail.$domain.pem $HOMEDIR/$user/conf/mail/$domain/ssl/$domain.pem
if [ -e "$HESTIA/data/users/$user/ssl/mail.$domain.ca" ]; then
cp -f $HESTIA/data/users/$user/ssl/mail.$domain.ca $HOMEDIR/$user/conf/mail/$domain/ssl/$domain.ca
fi
# Add domain SSL configuration to dovecot
if [ -f "/etc/dovecot/conf.d/domains/$domain.conf" ]; then
rm -f /etc/dovecot/conf.d/domains/$domain.conf
fi
echo "" >> /etc/dovecot/conf.d/domains/$domain.conf
echo "local_name mail.$domain {" >> /etc/dovecot/conf.d/domains/$domain.conf
echo " ssl_cert = <$HOMEDIR/$user/conf/mail/$domain/ssl/$domain.pem" >> /etc/dovecot/conf.d/domains/$domain.conf
echo " ssl_key = <$HOMEDIR/$user/conf/mail/$domain/ssl/$domain.key" >> /etc/dovecot/conf.d/domains/$domain.conf
echo "}" >> /etc/dovecot/conf.d/domains/$domain.conf
# Add domain SSL configuration to exim4
# Cleanup symlinks
find /usr/local/hestia/ssl/mail -xtype l -delete
ln -s -f $HOMEDIR/$user/conf/mail/$domain/ssl/$domain.pem /usr/local/hestia/ssl/mail/mail.$domain.crt
ln -s -f $HOMEDIR/$user/conf/mail/$domain/ssl/$domain.key /usr/local/hestia/ssl/mail/mail.$domain.key
# Set correct permissions on certificates
chmod 750 $HOMEDIR/$user/conf/mail/$domain/ssl
chown -R $MAIL_USER:mail $HOMEDIR/$user/conf/mail/$domain/ssl
chmod 0644 $HOMEDIR/$user/conf/mail/$domain/ssl/*
chown -h $user:mail $HOMEDIR/$user/conf/mail/$domain/ssl/*
chmod -R 0644 /usr/local/hestia/ssl/mail/*
chown -h $user:mail /usr/local/hestia/ssl/mail/*
fi
# Move data
mv $HOMEDIR/$owner/mail/$domain $HOMEDIR/$user/mail/
# Change ownership
find $HOMEDIR/$user/mail/$domain -user $owner \
-exec chown -h $user {} \;
# Checking exim username for later chowning
exim_user="exim"
check_exim_username=$(grep -c '^Debian-exim:' /etc/passwd)
if [ "$check_exim_username" -eq 1 ]; then
exim_user="Debian-exim"
fi
# Chowning mail conf files to exim user
if [ -d "$HOMEDIR/$user/conf/mail/$domain" ]; then
find $HOMEDIR/$user/conf/mail/$domain -user root \
-exec chown $exim_user {} \;
fi
# Remove old mail directory from original owner
if [ -e "$HOMEDIR/$owner/mail/$domain" ]; then
rm -rf "$HOMEDIR/$owner/mail/$domain"
fi
# Remove old mail configuration directory from original owner
if [ -e "$HOMEDIR/$owner/conf/mail/$domain" ]; then
rm -rf "$HOMEDIR/$owner/conf/mail/$domain"
fi
if [ -e "$HESTIA/data/users/$owner/mail/$domain.conf" ]; then
rm -f "$HESTIA/data/users/$owner/mail/$domain.conf"
fi
# Rebuild config
$BIN/v-unsuspend-mail-domain "$user" "$domain" no
$BIN/v-rebuild-mail-domains "$owner" no
$BIN/v-rebuild-mail-domains "$user"
fi
# Update counters
$BIN/v-update-user-counters "$owner"
$BIN/v-update-user-counters "$user"
# Mail domains currently don't have the IP variable set see #2306
if [ -n "$old_ip" ]; then
# Recalculate ip usage
if [ -n "$ip" ]; then
decrease_ip_value $old_ip $owner
increase_ip_value $new_ip
else
# recalculate ip
decrease_ip_value $old_ip $owner
increase_ip_value $old_ip
fi
fi
# Send notification to panel
if [ -n "$web_data" ] || [ -n "$dns_data" ] || [ -n "$mail_data" ]; then
$BIN/v-add-user-notification "$user" "$domain has been added to your account" ''
fi
#----------------------------------------------------------#
# Hestia #
#----------------------------------------------------------#
# Logging
$BIN/v-log-action "system" "Info" "System" "Domain moved between users (Domain: $domain, Old user: $owner, New User: $user)."
$BIN/v-log-action "$user" "Info" "System" "New domain added to account (Domain: $domain)."
$BIN/v-log-action "$owner" "Info" "System" "Domain removed from account (Domain: $domain)."
log_event "$OK" "$ARGUMENTS"
exit

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

Loading…
Cancel
Save