diff --git a/.gitignore b/.gitignore index ed8b453a..85a8ccc7 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ docs/src/man1/*.1 docs/src/man7/*.7 docs/src/man7/cdist-type__*.rst docs/src/cdist-reference.rst +docs/src/cdist-types.rst +docs/src/cdist.cfg.skeleton # Ignore cdist cache for version control /cache/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 44de6b0b..e215652c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,28 +1,20 @@ stages: - test +image: code.ungleich.ch:5050/ungleich-public/cdist/cdist-ci:latest + unit_tests: stage: test - before_script: - - 'apk update' - - 'apk add python3' script: - ./bin/build-helper version - ./bin/build-helper test pycodestyle: stage: test - before_script: - - 'apk update' - - 'apk add python3 py3-pycodestyle' - - 'ln -f -s /usr/bin/pycodestyle-3 /usr/bin/pycodestyle' script: - ./bin/build-helper pycodestyle shellcheck: stage: test - before_script: - - 'wget https://storage.googleapis.com/shellcheck/shellcheck-stable.linux.x86_64.tar.xz' - - 'tar xf shellcheck-stable.linux.x86_64.tar.xz && mv shellcheck-stable/shellcheck /usr/bin/' script: - ./bin/build-helper shellcheck diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..14682ad6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + cdist + Copyright (C) 2019 ungleich-public + + 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 . + +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: + + cdist Copyright (C) 2019 ungleich-public + 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 +. + + 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 +. diff --git a/Makefile b/Makefile index fa3327d1..f89ac1e7 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,18 @@ DOCSREFSH=$(DOCS_SRC_DIR)/cdist-reference.rst.sh $(DOCSREF): $(DOCSREFSH) $(DOCSREFSH) +# Html types list with references +DOCSTYPESREF=$(MAN7DSTDIR)/cdist-types.rst +DOCSTYPESREFSH=$(DOCS_SRC_DIR)/cdist-types.rst.sh + +$(DOCSTYPESREF): $(DOCSTYPESREFSH) + $(DOCSTYPESREFSH) + +DOCSCFGSKEL=./configuration/cdist.cfg.skeleton + +configskel: $(DOCSCFGSKEL) + cp -f "$(DOCSCFGSKEL)" "$(DOCS_SRC_DIR)/" + version: @[ -f "cdist/version.py" ] || { \ printf "Missing 'cdist/version.py', please generate it first.\n" && exit 1; \ @@ -72,7 +84,7 @@ version: man: version $(MANTYPES) $(DOCSREF) $(SPHINXM) -html: version $(MANTYPES) $(DOCSREF) +html: version configskel $(MANTYPES) $(DOCSREF) $(DOCSTYPESREF) $(SPHINXH) docs: man html @@ -114,6 +126,8 @@ speeches: $(SPEECHES) # clean: docs-clean rm -f $(DOCS_SRC_DIR)/cdist-reference.rst + rm -f $(DOCS_SRC_DIR)/cdist-types.rst + rm -f $(DOCS_SRC_DIR)/cdist.cfg.skeleton find "$(DOCS_SRC_DIR)" -mindepth 2 -type l \ | xargs rm -f diff --git a/bin/build-helper b/bin/build-helper index 7cfc4d55..ed41e438 100755 --- a/bin/build-helper +++ b/bin/build-helper @@ -74,6 +74,7 @@ SHELLCHECKCMD="shellcheck -s sh -f gcc -x" # Skip SC2154 for variables starting with __ since such variables are cdist # environment variables. SHELLCHECK_SKIP=': __.*is referenced but not assigned.*\[SC2154\]' +SHELLCHECKTMP=".shellcheck.tmp" # Change to checkout directory basedir="${0%/*}/../" @@ -369,7 +370,7 @@ eof cat << eof Manual steps post release: - cdist-web - - send mail body generated in mailinglist.tmp and inform Dmitry for deb + - send generated mailinglist.tmp mail - twitter eof ;; @@ -431,53 +432,67 @@ eof ;; shellcheck-global-explorers) - find cdist/conf/explorer -type f -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0 + # shellcheck disable=SC2086 + find cdist/conf/explorer -type f -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; shellcheck-type-explorers) - find cdist/conf/type -type f -path "*/explorer/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0 + # shellcheck disable=SC2086 + find cdist/conf/type -type f -path "*/explorer/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; shellcheck-manifests) - find cdist/conf/type -type f -name manifest -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0 + # shellcheck disable=SC2086 + find cdist/conf/type -type f -name manifest -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; shellcheck-local-gencodes) - find cdist/conf/type -type f -name gencode-local -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0 + # shellcheck disable=SC2086 + find cdist/conf/type -type f -name gencode-local -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; shellcheck-remote-gencodes) - find cdist/conf/type -type f -name gencode-remote -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0 + # shellcheck disable=SC2086 + find cdist/conf/type -type f -name gencode-remote -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; shellcheck-scripts) - ${SHELLCHECKCMD} scripts/cdist-dump scripts/cdist-new-type || exit 0 + # shellcheck disable=SC2086 + ${SHELLCHECKCMD} scripts/cdist-dump scripts/cdist-new-type > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; shellcheck-gencodes) - "$0" shellcheck-local-gencodes - "$0" shellcheck-remote-gencodes + "$0" shellcheck-local-gencodes || exit 1 + "$0" shellcheck-remote-gencodes || exit 1 ;; shellcheck-types) - "$0" shellcheck-type-explorers - "$0" shellcheck-manifests - "$0" shellcheck-gencodes + "$0" shellcheck-type-explorers || exit 1 + "$0" shellcheck-manifests || exit 1 + "$0" shellcheck-gencodes || exit 1 ;; shellcheck) - "$0" shellcheck-global-explorers - "$0" shellcheck-types - "$0" shellcheck-scripts + "$0" shellcheck-global-explorers || exit 1 + "$0" shellcheck-types || exit 1 + "$0" shellcheck-scripts || exit 1 ;; shellcheck-type-files) - find cdist/conf/type -type f -path "*/files/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" || exit 0 + # shellcheck disable=SC2086 + find cdist/conf/type -type f -path "*/files/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; shellcheck-with-files) - "$0" shellcheck - "$0" shellcheck-type-files + "$0" shellcheck || exit 1 + "$0" shellcheck-type-files || exit 1 ;; shellcheck-build-helper) @@ -535,6 +550,7 @@ eof # Temp files rm -f ./*.tmp + rm -f ./.*.tmp ;; distclean) diff --git a/cdist/argparse.py b/cdist/argparse.py index ca69cdae..611c484a 100644 --- a/cdist/argparse.py +++ b/cdist/argparse.py @@ -6,6 +6,7 @@ import collections import functools import cdist.configuration import cdist.preos +import cdist.info # set of beta sub-commands @@ -103,7 +104,7 @@ def get_parsers(): name="log level"), help=('Set the specified verbosity level. ' 'The levels, in order from the lowest to the highest, are: ' - 'ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) ' + 'ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3), ' 'TRACE (4 or higher). If used along with -v then -v ' 'increases last set value and -l overwrites last set ' 'value.'), @@ -424,7 +425,7 @@ def get_parsers(): parser['inventory'].set_defaults( func=cdist.inventory.Inventory.commandline) - # PreOs + # PreOS parser['preos'] = parser['sub'].add_parser('preos', add_help=False) # Shell @@ -436,6 +437,37 @@ def get_parsers(): ' should be POSIX compatible shell.')) parser['shell'].set_defaults(func=cdist.shell.Shell.commandline) + # Info + parser['info'] = parser['sub'].add_parser('info') + parser['info'].add_argument( + '-a', '--all', help='Display all info. This is the default.', + action='store_true', default=False) + parser['info'].add_argument( + '-c', '--conf-dir', + help='Add configuration directory (can be repeated).', + action='append') + parser['info'].add_argument( + '-e', '--global-explorers', + help='Display info for global explorers.', action='store_true', + default=False) + parser['info'].add_argument( + '-F', '--fixed-string', + help='Interpret pattern as a fixed string.', action='store_true', + default=False) + parser['info'].add_argument( + '-f', '--full', help='Display full details.', + action='store_true', default=False) + parser['info'].add_argument( + '-g', '--config-file', + help='Use specified custom configuration file.', + dest="config_file", required=False) + parser['info'].add_argument( + '-t', '--types', help='Display info for types.', + action='store_true', default=False) + parser['info'].add_argument( + 'pattern', nargs='?', help='Glob pattern.') + parser['info'].set_defaults(func=cdist.info.Info.commandline) + for p in parser: parser[p].epilog = EPILOG diff --git a/cdist/conf/explorer/disks b/cdist/conf/explorer/disks index 87a6b5c6..24540601 100755 --- a/cdist/conf/explorer/disks +++ b/cdist/conf/explorer/disks @@ -1,27 +1,67 @@ -#!/bin/sh +#!/bin/sh -e +# +# based on previous work by other people, modified by: +# 2020 Dennis Camera +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# +# Finds disks of the system (excl. ram disks, floppy, cdrom) uname_s="$(uname -s)" -case "${uname_s}" in +case $uname_s in FreeBSD) sysctl -n kern.disks ;; - OpenBSD|NetBSD) - sysctl -n hw.disknames | grep -Eo '[lsw]d[0-9]+' | xargs + OpenBSD) + sysctl -n hw.disknames | grep -Eo '[lsw]d[0-9]+' + ;; + NetBSD) + PATH="${PATH}:/usr/local/sbin:/usr/sbin:/sbin" + sysctl -n hw.disknames \ + | awk 'BEGIN { RS = " " } /^[lsw]d[0-9]+/' ;; Linux) - if command -v lsblk > /dev/null + # list of major device numbers toexclude: + # ram disks, floppies, cdroms + # https://www.kernel.org/doc/Documentation/admin-guide/devices.txt + ign_majors='1 2 11' + + if command -v lsblk >/dev/null 2>&1 then - # exclude ram disks, floppies and cdroms - # https://www.kernel.org/doc/Documentation/admin-guide/devices.txt - lsblk -e 1,2,11 -dno name | xargs + lsblk -e "$(echo "$ign_majors" | tr ' ' ',')" -dno name + elif test -d /sys/block/ + then + # shellcheck disable=SC2012 + ls -1 /sys/block/ \ + | awk -v ign_majors="$(echo "$ign_majors" | tr ' ' '|')" ' + { + devfile = "/sys/block/" $0 "/dev" + getline devno < devfile + close(devfile) + if (devno !~ "^(" ign_majors "):") print + }' else - printf "Don't know how to list disks for %s operating system without lsblk, if you can please submit a patch\n" "${uname_s}" >&2 + echo "Don't know how to list disks on Linux without lsblk and sysfs." >&2 + echo 'If you can, please submit a patch.'>&2 fi ;; *) - printf "Don't know how to list disks for %s operating system, if you can please submit a patch\n" "${uname_s}" >&2 + printf "Don't know how to list disks for %s operating system.\n" "${uname_s}" >&2 + printf 'If you can please submit a patch\n' >&2 ;; -esac - -exit 0 +esac \ +| xargs diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init index a8a7857e..f27c77ef 100755 --- a/cdist/conf/explorer/init +++ b/cdist/conf/explorer/init @@ -1,7 +1,8 @@ -#!/bin/sh +#!/bin/sh -e # # 2016 Daniel Heule (hda at sfs.biz) # Copyright 2017, Philippe Gregoire +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,21 +20,423 @@ # along with cdist. If not, see . # # -# Returns the process name of pid 1 ( normaly the init system ) -# for example at linux this value is "init" or "systemd" in most cases +# Returns the name of the init system (PID 1) + +# Expected values: +# Linux: +# Adélie Linux: +# sysvinit+openrc +# Alpine Linux: +# busybox-init+openrc +# ArchLinux: +# systemd, sysvinit +# CRUX: +# sysvinit +# Debian: +# systemd, upstart, sysvinit, openrc, ??? +# Devuan: +# sysvinit, sysvinit+openrc +# Gentoo: +# sysvinit+openrc, openrc-init, systemd +# OpenBMC: +# systemd +# OpenWrt: +# procd, init??? +# RedHat (RHEL, CentOS, Fedora, RedHat Linux, ...): +# systemd, upstart, upstart-legacy, sysvinit +# Slackware: +# sysvinit +# SuSE: +# systemd, sysvinit +# Ubuntu: +# systemd, upstart, upstart-legacy, sysvinit +# VoidLinux: +# runit # +# GNU: +# Debian: +# sysvinit, hurd-init +# +# BSD: +# {Free,Open,Net}BSD: +# init +# +# Mac OS X: +# launchd, init+SystemStarter +# +# Solaris/Illumos: +# smf, init??? -uname_s="$(uname -s)" +# NOTE: init systems can be stacked. This is popular to run OpenRC on top of +# sysvinit (Gentoo) or busybox-init (Alpine), but can also be used to run runit +# as a systemd service. This makes init system detection very complicated +# (which result is expected?) This script tries to untangle some combinations, +# OpenRC on top of sysv or busybox (X+openrc), but will ignore others (runit as +# a systemd service) -case "$uname_s" in - Linux) - (pgrep -P0 -l | awk '/^1[ \t]/ {print $2;}') || true - ;; - FreeBSD|OpenBSD) - ps -o comm= -p 1 || true - ;; - *) - # return a empty string as unknown value - echo "" - ;; -esac +# NOTE: When we have no idea, nothing will be printed! + +# NOTE: +# When trying to gather information about the init system make sure to do so +# without calling the binary! On some systems this triggers a reinitialisation +# of the system which we don't want (e.g. embedded systems). + + +set -e + +KERNEL_NAME=$(uname -s) + +KNOWN_INIT_SYSTEMS=$(cat </dev/null 2>&1 || return 1 + launchctl getenv PATH >/dev/null || return 1 + echo launchd +} + +check_openrc() { + test -f /run/openrc/softlevel || return 1 + echo openrc +} + +check_procd() ( + procd_path=${1:-/sbin/procd} + test -x "${procd_path}" || return 1 + grep -q 'procd' "${procd_path}" || return 1 + echo procd +) + +check_runit() { + test -d /run/runit || return 1 + echo runit +} + +check_smf() { + # XXX: Is this the correct way?? + test -f /etc/svc/volatile/svc_nonpersist.db || return 1 + echo smf +} + +check_systemd() { + # NOTE: sd_booted(3) + test -d /run/systemd/system/ || return 1 + # systemctl --version | sed -e '/^systemd/!d;s/^systemd //' + echo systemd +} + +check_systemstarter() { + test -d /System/Library/StartupItems/ || return 1 + test -f /System/Library/StartupItems/LoginWindow/StartupParameters.plist || return 1 + echo init+SystemStarter +} + +check_sysvinit() ( + init_path=${1:-/sbin/init} + test -x "${init_path}" || return 1 + grep -q 'INIT_VERSION=sysvinit-[0-9.]*' "${init_path}" || return 1 + + # It is quite common to use SysVinit to stack other init systemd + # (like OpenRC) on top of it. So we check for that, too. + if stacked=$(check_openrc) + then + echo "sysvinit+${stacked}" + else + echo sysvinit + fi + unset stacked +) + +check_upstart() { + test -x "$(command -v initctl)" || return 1 + case $(initctl version) + in + *'(upstart '*')') + if test -d /etc/init + then + # modern (DBus-based?) upstart >= 0.5 + echo upstart + elif test -d /etc/event.d + then + # ancient upstart + echo upstart-legacy + else + # whatever... + echo upstart + fi + ;; + *) + return 1 + ;; + esac +} + +find_init_procfs() ( + # First, check if the required file in procfs exists... + test -h /proc/1/exe || return 1 + + # Find init executable + init_exe=$(ls -l /proc/1/exe 2>/dev/null) || return 1 + init_exe=${init_exe#* -> } + + if ! test -x "$init_exe" + then + # On some rare occasions it can happen that the + # running init's binary has been replaced. In this + # case Linux adjusts the symlink to "X (deleted)" + + # [root@fedora-12 ~]# readlink /proc/1/exe + # /sbin/init (deleted) + # [root@fedora-12 ~]# ls -l /proc/1/exe + # lrwxrwxrwx. 1 root root 0 2020-01-30 23:00 /proc/1/exe -> /sbin/init (deleted) + + init_exe=${init_exe% (deleted)} + test -x "$init_exe" || return 1 + fi + + echo "${init_exe}" +) + +guess_by_path() { + case $1 + in + /bin/busybox) + check_busybox_init "$1" && return + ;; + /lib/systemd/systemd) + check_systemd "$1" && return + ;; + /hurd/init) + check_hurd_init "$1" && return + ;; + /sbin/launchd) + check_launchd "$1" && return + ;; + /usr/bin/runit|/sbin/runit) + check_runit "$1" && return + ;; + /sbin/openrc-init) + if check_openrc "$1" >/dev/null + then + echo openrc-init + return + fi + ;; + /sbin/procd) + check_procd "$1" && return + ;; + /sbin/init|*/init) + # init: it could be anything -> (explicit) no match + return 1 + ;; + esac + + # No match + return 1 +} + +guess_by_comm_name() { + case $1 + in + busybox) + check_busybox_init && return + ;; + openrc-init) + if check_openrc >/dev/null + then + echo openrc-init + return 0 + fi + ;; + init) + # init could be anything -> no match + return 1 + ;; + *) + # Run check function by comm name if available. + # Fall back to comm name if either it does not exist or + # returns non-zero. + if type "check_$1" >/dev/null + then + "check_$1" && return + else + echo "$1" ; return 0 + fi + esac + + return 1 +} + +check_list() ( + # List must be a multi-line input on stdin (one name per line) + while read -r init + do + "check_${init}" || continue + return 0 + done + return 1 +) + + +# BusyBox's versions of ps and pgrep do not support some options +# depending on which compile-time options have been used. + +find_init_pgrep() { + pgrep -P0 -fl 2>/dev/null | awk -F '[[:blank:]]' '$1 == 1 { print $2 }' +} + +find_init_ps() { + case $KERNEL_NAME + in + Darwin) + ps -o command -p 1 2>/dev/null | tail -n +2 + ;; + FreeBSD) + ps -o args= -p 1 2>/dev/null | cut -d ' ' -f 1 + ;; + Linux) + ps -o comm= -p 1 2>/dev/null + ;; + NetBSD) + ps -o comm= -p 1 2>/dev/null + ;; + OpenBSD) + ps -o args -p 1 2>/dev/null | tail -n +2 | cut -d ' ' -f 1 + ;; + *) + ps -o args= -p 1 2>/dev/null + ;; + esac | trim # trim trailing whitespace (some ps like Darwin add it) +} + +find_init() { + case $KERNEL_NAME + in + Linux|GNU|NetBSD) + find_init_procfs || find_init_pgrep || find_init_ps + ;; + FreeBSD) + find_init_procfs || find_init_ps + ;; + OpenBSD) + find_init_pgrep || find_init_ps + ;; + Darwin|SunOS) + find_init_ps + ;; + *) + echo "Don't know how to determine init." >&2 + echo 'Please send a patch.' >&2 + exit 1 + esac +} + +# ----- + +init=$(find_init) + +# If we got a path, guess by the path first (fall back to file name if no match) +# else guess by file name directly. +# shellcheck disable=SC2015 +{ + test -x "${init}" \ + && guess_by_path "${init}" \ + || guess_by_comm_name "$(basename "${init}")" +} && exit 0 || true + + +# Guessing based on the file path and name didn’t lead to a definitive result. +# +# We go through all of the checks until we find a match. To speed up the +# process, common cases will be checked first based on the underlying kernel. + +{ common_candidates_by_kernel; echo "${KNOWN_INIT_SYSTEMS}"; } \ + | unique | check_list diff --git a/cdist/conf/explorer/os_release b/cdist/conf/explorer/os_release index cfc01004..6489446b 100644 --- a/cdist/conf/explorer/os_release +++ b/cdist/conf/explorer/os_release @@ -1,6 +1,7 @@ #!/bin/sh # # 2018 Adam Dej (dejko.a at gmail.com) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -21,6 +22,17 @@ # See os-release(5) and http://0pointer.de/blog/projects/os-release -set +e +if test -f /etc/os-release +then + # Linux and FreeBSD (usually a symlink) + cat /etc/os-release +elif test -f /usr/lib/os-release +then + # systemd + cat /usr/lib/os-release +elif test -f /var/run/os-release +then + # FreeBSD (created by os-release service) + cat /var/run/os-release +fi -cat /etc/os-release || cat /usr/lib/os-release || true diff --git a/cdist/conf/explorer/os_version b/cdist/conf/explorer/os_version index 4c41695b..1d54ea60 100755 --- a/cdist/conf/explorer/os_version +++ b/cdist/conf/explorer/os_version @@ -70,4 +70,7 @@ case "$("$__explorer/os")" in ubuntu) lsb_release -sr ;; -esac + alpine) + cat /etc/alpine-release + ;; +esac \ No newline at end of file diff --git a/cdist/conf/type/__acl/gencode-remote b/cdist/conf/type/__acl/gencode-remote index 6dab4d09..e5404a9d 100755 --- a/cdist/conf/type/__acl/gencode-remote +++ b/cdist/conf/type/__acl/gencode-remote @@ -20,7 +20,13 @@ file_is="$( cat "$__object/explorer/file_is" )" -[ "$file_is" = 'missing' ] && [ -z "$__cdist_dry_run" ] && exit 0 +if [ "$file_is" = 'missing' ] \ + && [ -z "$__cdist_dry_run" ] \ + && \( [ ! -f "$__object/parameter/file" ] \ + || [ ! -f "$__object/parameter/directory" ] \) +then + exit 0 +fi os="$( cat "$__global/explorer/os" )" @@ -28,7 +34,20 @@ acl_path="/$__object_id" acl_is="$( cat "$__object/explorer/acl_is" )" -if [ -f "$__object/parameter/acl" ] +if [ -f "$__object/parameter/source" ] +then + acl_source="$( cat "$__object/parameter/source" )" + + if [ "$acl_source" = '-' ] + then + acl_should="$( cat "$__object/stdin" )" + else + acl_should="$( grep -Ev '^#|^$' "$acl_source" )" + fi +elif [ -f "$__object/parameter/entry" ] +then + acl_should="$( cat "$__object/parameter/entry" )" +elif [ -f "$__object/parameter/acl" ] then acl_should="$( cat "$__object/parameter/acl" )" elif diff --git a/cdist/conf/type/__acl/man.rst b/cdist/conf/type/__acl/man.rst index 85e946ce..28412871 100644 --- a/cdist/conf/type/__acl/man.rst +++ b/cdist/conf/type/__acl/man.rst @@ -15,10 +15,24 @@ See ``setfacl`` and ``acl`` manpages for more details. REQUIRED MULTIPLE PARAMETERS ---------------------------- -acl +entry Set ACL entry following ``getfacl`` output syntax. +OPTIONAL PARAMETERS +------------------- +source + Read ACL entries from stdin or file. + Ordering of entries is not important. + When reading from file, comments and empty lines are ignored. + +file + Create/change file with ``__file`` using ``user:group:mode`` pattern. + +directory + Create/change directory with ``__directory`` using ``user:group:mode`` pattern. + + BOOLEAN PARAMETERS ------------------ default @@ -36,8 +50,8 @@ remove DEPRECATED PARAMETERS --------------------- -Parameters ``user``, ``group``, ``mask`` and ``other`` are deprecated and they -will be removed in future versions. Please use ``acl`` parameter instead. +Parameters ``acl``, ``user``, ``group``, ``mask`` and ``other`` are deprecated and they +will be removed in future versions. Please use ``entry`` parameter instead. EXAMPLES @@ -49,27 +63,38 @@ EXAMPLES --default \ --recursive \ --remove \ - --acl user:alice:rwx \ - --acl user:bob:r-x \ - --acl group:project-group:rwx \ - --acl group:some-other-group:r-x \ - --acl mask::r-x \ - --acl other::r-x + --entry user:alice:rwx \ + --entry user:bob:r-x \ + --entry group:project-group:rwx \ + --entry group:some-other-group:r-x \ + --entry mask::r-x \ + --entry other::r-x # give Alice read-only access to subdir, # but don't allow her to see parent content. __acl /srv/project2 \ --remove \ - --acl default:group:secret-project:rwx \ - --acl group:secret-project:rwx \ - --acl user:alice:--x + --entry default:group:secret-project:rwx \ + --entry group:secret-project:rwx \ + --entry user:alice:--x __acl /srv/project2/subdir \ --default \ --remove \ - --acl group:secret-project:rwx \ - --acl user:alice:r-x + --entry group:secret-project:rwx \ + --entry user:alice:r-x + + # read acl from stdin + echo 'user:alice:rwx' \ + | __acl /path/to/directory --source - + + # create/change directory too + __acl /path/to/directory \ + --default \ + --remove \ + --directory root:root:770 \ + --entry user:nobody:rwx AUTHORS diff --git a/cdist/conf/type/__acl/manifest b/cdist/conf/type/__acl/manifest new file mode 100755 index 00000000..5fd23110 --- /dev/null +++ b/cdist/conf/type/__acl/manifest @@ -0,0 +1,11 @@ +#!/bin/sh -e + +for p in file directory +do + [ ! -f "$__object/parameter/$p" ] && continue + + "__$p" "/$__object_id" \ + --owner "$( awk -F: '{print $1}' "$__object/parameter/$p" )" \ + --group "$( awk -F: '{print $2}' "$__object/parameter/$p" )" \ + --mode "$( awk -F: '{print $3}' "$__object/parameter/$p" )" +done diff --git a/cdist/conf/type/__acl/parameter/deprecated/acl b/cdist/conf/type/__acl/parameter/deprecated/acl new file mode 100644 index 00000000..94e14159 --- /dev/null +++ b/cdist/conf/type/__acl/parameter/deprecated/acl @@ -0,0 +1 @@ +see manual for details diff --git a/cdist/conf/type/__acl/parameter/optional b/cdist/conf/type/__acl/parameter/optional index 4b32086b..cdcbc0b8 100644 --- a/cdist/conf/type/__acl/parameter/optional +++ b/cdist/conf/type/__acl/parameter/optional @@ -1,2 +1,5 @@ mask other +source +file +directory diff --git a/cdist/conf/type/__acl/parameter/optional_multiple b/cdist/conf/type/__acl/parameter/optional_multiple index 95c25d55..c615d507 100644 --- a/cdist/conf/type/__acl/parameter/optional_multiple +++ b/cdist/conf/type/__acl/parameter/optional_multiple @@ -1,3 +1,4 @@ +entry acl user group diff --git a/cdist/conf/type/__apt_unattended_upgrades/man.rst b/cdist/conf/type/__apt_unattended_upgrades/man.rst new file mode 100644 index 00000000..2231b5f9 --- /dev/null +++ b/cdist/conf/type/__apt_unattended_upgrades/man.rst @@ -0,0 +1,68 @@ +cdist-type__apt_unattended_upgrades(7) +====================================== + +NAME +---- +cdist-type__apt_unattended_upgrades - automatic installation of updates + + +DESCRIPTION +----------- + +Install and configure unattended-upgrades package. + +For more information see https://wiki.debian.org/UnattendedUpgrades. + + +OPTIONAL MULTIPLE PARAMETERS +---------------------------- +option + Set options for unattended-upgrades. See examples. + + Supported options with default values (as of 2020-01-17) are: + + - AutoFixInterruptedDpkg, default is "true" + - MinimalSteps, default is "true" + - InstallOnShutdown, default is "false" + - Mail, default is "" (empty) + - MailOnlyOnError, default is "false" + - Remove-Unused-Kernel-Packages, default is "true" + - Remove-New-Unused-Dependencies, default is "true" + - Remove-Unused-Dependencies, default is "false" + - Automatic-Reboot, default is "false" + - Automatic-Reboot-WithUsers, default is "true" + - Automatic-Reboot-Time, default is "02:00" + - SyslogEnable, default is "false" + - SyslogFacility, default is "daemon" + - OnlyOnACPower, default is "true" + - Skip-Updates-On-Metered-Connections, default is "true" + - Verbose, default is "false" + - Debug, default is "false" + +blacklist + Python regular expressions, matching packages to exclude from upgrading. + + +EXAMPLES +-------- + +.. code-block:: sh + + __apt_unattended_upgrades \ + --option Mail=root \ + --option MailOnlyOnError=true \ + --blacklist multipath-tools \ + --blacklist open-iscsi + + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2020 Ander Punnar. 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. diff --git a/cdist/conf/type/__apt_unattended_upgrades/manifest b/cdist/conf/type/__apt_unattended_upgrades/manifest new file mode 100755 index 00000000..3c00e2f4 --- /dev/null +++ b/cdist/conf/type/__apt_unattended_upgrades/manifest @@ -0,0 +1,80 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +__package unattended-upgrades + +export require='__package/unattended-upgrades' + +# in normal circumstances 20auto-upgrades is managed +# by debconf and it can only contain these lines + +__file /etc/apt/apt.conf.d/20auto-upgrades \ + --owner root \ + --group root \ + --mode 644 \ + --source - << EOF +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Unattended-Upgrade "1"; +EOF + +# lets not write into upstream 50unattended-upgrades file, +# but use our own config file to avoid clashes + +conf_file='/etc/apt/apt.conf.d/51unattended-upgrades-cdist' + +conf='# this file is managed by cdist' + +if [ -f "$__object/parameter/option" ] +then + o='' + + while read -r l + do + o="$( printf '%s\nUnattended-Upgrade::%s "%s";\n' "$o" "${l%%=*}" "${l#*=}" )" + done \ + < "$__object/parameter/option" + + conf="$( printf '%s\n%s\n' "$conf" "$o" )" +fi + +if [ -f "$__object/parameter/blacklist" ] +then + b='Unattended-Upgrade::Package-Blacklist {' + + while read -r l + do + b="$( printf '%s\n"%s";\n' "$b" "$l" )" + done \ + < "$__object/parameter/blacklist" + + conf="$( printf '%s\n%s\n}\n' "$conf" "$b" )" +fi + +if [ "$( echo "$conf" | wc -l )" -gt 1 ] +then + echo "$conf" \ + | __file "$conf_file" \ + --owner root \ + --group root \ + --mode 644 \ + --source - +else + __file "$conf_file" --state absent +fi diff --git a/cdist/conf/type/__apt_unattended_upgrades/parameter/optional_multiple b/cdist/conf/type/__apt_unattended_upgrades/parameter/optional_multiple new file mode 100644 index 00000000..ea4fba2b --- /dev/null +++ b/cdist/conf/type/__apt_unattended_upgrades/parameter/optional_multiple @@ -0,0 +1,2 @@ +option +blacklist diff --git a/cdist/conf/type/__apt_unattended_upgrades/singleton b/cdist/conf/type/__apt_unattended_upgrades/singleton new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__cdist/manifest b/cdist/conf/type/__cdist/manifest index a97cf288..0b0f1263 100755 --- a/cdist/conf/type/__cdist/manifest +++ b/cdist/conf/type/__cdist/manifest @@ -37,6 +37,7 @@ source="$(cat "$__object/parameter/source")" # out of it home=/home/$username +# shellcheck disable=SC2086 __user "$username" --home "$home" $shell require="__user/$username" __directory "$home" \ diff --git a/cdist/conf/type/__consul_agent/man.rst b/cdist/conf/type/__consul_agent/man.rst index 966abc60..62ee70bb 100644 --- a/cdist/conf/type/__consul_agent/man.rst +++ b/cdist/conf/type/__consul_agent/man.rst @@ -116,6 +116,9 @@ verify-incoming verify-outgoing enforce the use of TLS and verify the peers authenticity on outgoing connections +use-distribution-package + uses distribution package instead of upstream binary + EXAMPLES -------- diff --git a/cdist/conf/type/__consul_agent/manifest b/cdist/conf/type/__consul_agent/manifest index a88d26ed..7b54529c 100755 --- a/cdist/conf/type/__consul_agent/manifest +++ b/cdist/conf/type/__consul_agent/manifest @@ -1,7 +1,8 @@ #!/bin/sh -e # # 2015 Steven Armstrong (steven-cdist at armstrong.cc) -# 2015-2019 Nico Schottelius (nico-cdist at schottelius.org) +# 2015-2020 Nico Schottelius (nico-cdist at schottelius.org) +# 2019 Timothée Floure (timothee.floure at ungleich.ch) # # This file is part of cdist. # @@ -19,133 +20,87 @@ # along with cdist. If not, see . # - os=$(cat "$__global/explorer/os") -case "$os" in - alpine|scientific|centos|debian|devuan|redhat|ubuntu) - # whitelist safeguard - : - ;; - *) - echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2 - echo "Please contribute an implementation for it if you can." >&2 - exit 1 - ;; -esac +### +# Type parameters. state="$(cat "$__object/parameter/state")" user="$(cat "$__object/parameter/user")" group="$(cat "$__object/parameter/group")" -data_dir="/var/lib/consul" -conf_dir="/etc/consul/conf.d" -conf_file="config.json" +release=$(cat "$__global/explorer/lsb_release") +if [ -f "$__object/parameter/use-distribution-package" ]; then + use_distribution_package=1 +fi -# FIXME: there has got to be a better way to handle the dependencies in this case -case "$state" in - present) - __group "$group" --system --state "$state" - require="__group/$group" \ - __user "$user" --system --gid "$group" \ - --home "$data_dir" --state "$state" - export require="__user/consul" - ;; - absent) - echo "Sorry, state=absent currently not supported :-(" >&2 - exit 1 - require="$__object_name" \ - __user "$user" --system --gid "$group" --state "$state" - require="__user/$user" \ - __group "$group" --system --state "$state" - ;; +### +# Those are default that might be overriden by os-specific logic. + +data_dir="/var/lib/consul" + + + +tls_dir="$conf_dir/tls" + +case "$os" in + alpine) + conf_dir="/etc/consul" + conf_file="server.json" + ;; + *) + conf_dir="/etc/consul/conf.d" + conf_file="config.json" + ;; esac -__directory /etc/consul \ - --owner root --group "$group" --mode 750 --state "$state" -require="__directory/etc/consul" \ - __directory "$conf_dir" \ - --owner root --group "$group" --mode 750 --state "$state" +### +# Sane deployment, based on distribution package when available. -if [ -f "$__object/parameter/ca-file-source" ] || [ -f "$__object/parameter/cert-file-source" ] || [ -f "$__object/parameter/key-file-source" ]; then - # create directory for ssl certs - require="__directory/etc/consul" \ - __directory /etc/consul/ssl \ - --owner root --group "$group" --mode 750 --state "$state" -fi +distribution_setup () { + case "$os" in + debian) + # consul is only available starting Debian 10 (buster). + # See https://packages.debian.org/buster/consul + if [ "$release" -lt 10 ]; then + echo "Consul is not available for your debian release." >&2 + echo "Please use the 'manual' (i.e. non-package) installation or \ + upgrade the target system." >&2 + exit 1 + fi -__directory "$data_dir" \ - --owner "$user" --group "$group" --mode 770 --state "$state" + # Override previously defined environment to match debian packaging. + conf_dir='/etc/consul.d' + user='consul' + group='consul' + ;; + alpine) + # consul is only available starting Alpine 3.12 (= edge during the 3.11 cycle). + # See https://pkgs.alpinelinux.org/packages?name=consul&branch=edge + # Override previously defined environment to match alpine packaging. + conf_dir='/etc/consul' + conf_file='server.json' + data_dir='/var/consul' + user='consul' + group='consul' + ;; + *) + echo "Your operating system ($os) is currently not supported with the \ + --use-distribution-package flag (${__type##*/})." >&2 + echo "Please use non-package installation or contribute an \ + implementation for if you can." >&2 + exit 1 + ;; + esac -# Generate json config file -( -echo "{" + # Install consul package. + __package consul --state "$state" -# parameters we define ourself -printf ' "data_dir": "%s"\n' "$data_dir" + export config_deployment_requires="__package/consul" +} -cd "$__object/parameter/" -for param in *; do - case "$param" in - state|user|group|json-config) continue ;; - ca-file-source|cert-file-source|key-file-source) - source="$(cat "$__object/parameter/$param")" - destination="/etc/consul/ssl/${source##*/}" - require="__directory/etc/consul/ssl" \ - __file "$destination" \ - --owner root --group consul --mode 640 \ - --source "$source" \ - --state "$state" - key="$(echo "${param%-*}" | tr '-' '_')" - printf ' ,"%s": "%s"\n' "$key" "$destination" - ;; - disable-remote-exec|disable-update-check|leave-on-terminate|rejoin-after-leave|server|enable-syslog|verify-incoming|verify-outgoing) - # handle boolean parameters - key="$(echo "$param" | tr '-' '_')" - printf ' ,"%s": true\n' "$key" - ;; - retry-join) - # join multiple parameters into json array - retry_join="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join")" - # remove trailing , - printf ' ,"retry_join": [%s]\n' "${retry_join%*,}" - ;; - retry-join-wan) - # join multiple parameters into json array over wan - retry_join_wan="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join-wan")" - # remove trailing , - printf ' ,"retry_join_wan": [%s]\n' "${retry_join_wan%*,}" - ;; - bootstrap-expect) - # integer key=value parameters - key="$(echo "$param" | tr '-' '_')" - printf ' ,"%s": %s\n' "$key" "$(cat "$__object/parameter/$param")" - ;; - *) - # string key=value parameters - key="$(echo "$param" | tr '-' '_')" - printf ' ,"%s": "%s"\n' "$key" "$(cat "$__object/parameter/$param")" - ;; - esac -done -if [ -f "$__object/parameter/json-config" ]; then - json_config="$(cat "$__object/parameter/json-config")" - if [ "$json_config" = "-" ]; then - json_config="$__object/stdin" - fi - # remove leading and trailing whitespace and commas from first and last line - # indent each line with 3 spaces for consistency - json=$(sed -e 's/^[ \t]*/ /' -e '1s/^[ \t,]*//' -e '$s/[ \t,]*$//' "$json_config") - printf ' ,%s\n' "$json" -fi -echo "}" -) | \ -require="__directory${conf_dir}" \ - __config_file "${conf_dir}/${conf_file}" \ - --owner root --group "$group" --mode 640 \ - --state "$state" \ - --onchange 'service consul status >/dev/null && service consul reload || true' \ - --source - +### +# LEGACY manual deployment, kept for compatibility reasons. init_sysvinit() { @@ -179,47 +134,186 @@ init_upstart() require="__file/etc/init/consul.conf" __start_on_boot consul } -# Install init script to start on boot -case "$os" in - alpine|devuan) - init_sysvinit debian - ;; - centos|redhat) - os_version="$(sed 's/[^0-9.]//g' "$__global/explorer/os_version")" - major_version="${os_version%%.*}" - case "$major_version" in - [456]) - init_sysvinit redhat - ;; - 7) - init_systemd - ;; - *) - echo "Unsupported CentOS/Redhat version: $os_version" >&2 - exit 1 - ;; - esac - ;; +manual_setup () { + case "$os" in + alpine|scientific|centos|debian|devuan|redhat|ubuntu) + # whitelist safeguard + : + ;; + *) + echo "Your operating system ($os) is currently not supported by this \ + type (${__type##*/})." >&2 + echo "Please contribute an implementation for it if you can." >&2 + exit 1 + ;; + esac - debian) - os_version=$(cat "$__global/explorer/os_version") - major_version="${os_version%%.*}" + # FIXME: there has got to be a better way to handle the dependencies in this case + case "$state" in + present) + __group "$group" --system --state "$state" + require="__group/$group" __user "$user" \ + --system --gid "$group" --home "$data_dir" --state "$state" + ;; + *) + echo "The $state state is not (yet?) supported by this type." >&2 + exit 1 + ;; + esac - case "$major_version" in - [567]) - init_sysvinit debian - ;; - [89]) - init_systemd - ;; - *) - echo "Unsupported Debian version $os_version" >&2 - exit 1 - ;; - esac - ;; + # Create data directory. + require="__user/consul" __directory "$data_dir" \ + --owner "$user" --group "$group" --mode 770 --state "$state" - ubuntu) - init_upstart + # Create config directory. + require="__user/consul" __directory "$conf_dir" \ + --parents --owner root --group "$group" --mode 750 --state "$state" + + # Install init script to start on boot + case "$os" in + devuan) + init_sysvinit debian + ;; + centos|redhat) + os_version="$(sed 's/[^0-9.]//g' "$__global/explorer/os_version")" + major_version="${os_version%%.*}" + case "$major_version" in + [456]) + init_sysvinit redhat + ;; + 7) + init_systemd + ;; + *) + echo "Unsupported CentOS/Redhat version: $os_version" >&2 + exit 1 + ;; + esac + ;; + + debian) + os_version=$(cat "$__global/explorer/os_version") + major_version="${os_version%%.*}" + + case "$major_version" in + [567]) + init_sysvinit debian + ;; + [89]|10) + init_systemd + ;; + *) + echo "Unsupported Debian version $os_version" >&2 + exit 1 + ;; + esac + ;; + + ubuntu) + init_upstart + ;; + esac + + config_deployment_requires="__user/consul __directory/$conf_dir" +} + +### +# Trigger requested installation method. +if [ $use_distribution_package ]; then + distribution_setup +else + manual_setup +fi + +### +# Install TLS certificates. + +if [ -f "$__object/parameter/ca-file-source" ] || \ + [ -f "$__object/parameter/cert-file-source" ] || \ + [ -f "$__object/parameter/key-file-source" ]; then + + requires="$config_deployment_requires" __directory "$tls_dir" \ + --owner root --group "$group" --mode 750 --state "$state" + + # Append to service restart requirements. + restart_requires="$restart_requires __directory/$conf_dir/tls" +fi + +### +# Generate and deploy configuration. + +json_configuration=$( + echo "{" + + # parameters we define ourself + printf ' "data_dir": "%s"\n' "$data_dir" + + cd "$__object/parameter/" + for param in *; do + case "$param" in + state|user|group|json-config|use-distribution-package) continue ;; + ca-file-source|cert-file-source|key-file-source) + source="$(cat "$__object/parameter/$param")" + destination="$tls_dir/${source##*/}" + require="__directory/$tls_dir" \ + __file "$destination" \ + --owner root --group consul --mode 640 \ + --source "$source" \ + --state "$state" + key="$(echo "${param%-*}" | tr '-' '_')" + printf ' ,"%s": "%s"\n' "$key" "$destination" ;; -esac + disable-remote-exec|disable-update-check|leave-on-terminate\ + |rejoin-after-leave|server|enable-syslog|verify-incoming|verify-outgoing) + # handle boolean parameters + key="$(echo "$param" | tr '-' '_')" + printf ' ,"%s": true\n' "$key" + ;; + retry-join) + # join multiple parameters into json array + retry_join="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join")" + # remove trailing , + printf ' ,"retry_join": [%s]\n' "${retry_join%*,}" + ;; + retry-join-wan) + # join multiple parameters into json array over wan + retry_join_wan="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join-wan")" + # remove trailing , + printf ' ,"retry_join_wan": [%s]\n' "${retry_join_wan%*,}" + ;; + bootstrap-expect) + # integer key=value parameters + key="$(echo "$param" | tr '-' '_')" + printf ' ,"%s": %s\n' "$key" "$(cat "$__object/parameter/$param")" + ;; + *) + # string key=value parameters + key="$(echo "$param" | tr '-' '_')" + printf ' ,"%s": "%s"\n' "$key" "$(cat "$__object/parameter/$param")" + ;; + esac + done + if [ -f "$__object/parameter/json-config" ]; then + json_config="$(cat "$__object/parameter/json-config")" + if [ "$json_config" = "-" ]; then + json_config="$__object/stdin" + fi + # remove leading and trailing whitespace and commas from first and last line + # indent each line with 3 spaces for consistency + json=$(sed -e 's/^[ \t]*/ /' -e '1s/^[ \t,]*//' -e '$s/[ \t,]*$//' "$json_config") + printf ' ,%s\n' "$json" + fi + echo "}" +) +echo "$json_configuration" | require="$config_deployment_requires" \ + __file "$conf_dir/$conf_file" \ + --owner root --group "$group" --mode 640 \ + --state "$state" \ + --source - + +# Set configuration deployment as requirement for service restart. +restart_requires="__file/$conf_dir/$conf_file" + +### +# Restart consul agent after everything else. +require="$restart_requires" __service consul --action restart diff --git a/cdist/conf/type/__consul_agent/parameter/boolean b/cdist/conf/type/__consul_agent/parameter/boolean index 91f7f17e..c86853c3 100644 --- a/cdist/conf/type/__consul_agent/parameter/boolean +++ b/cdist/conf/type/__consul_agent/parameter/boolean @@ -6,3 +6,4 @@ server enable-syslog verify-incoming verify-outgoing +use-distribution-package diff --git a/cdist/conf/type/__consul_check/explorer/conf-dir b/cdist/conf/type/__consul_check/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_check/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_check/manifest b/cdist/conf/type/__consul_check/manifest index c9f7add9..522aa1a9 100755 --- a/cdist/conf/type/__consul_check/manifest +++ b/cdist/conf/type/__consul_check/manifest @@ -19,7 +19,7 @@ # name="$(cat "$__object/parameter/name" 2>/dev/null || echo "$__object_id")" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="check_${name}.json" state="$(cat "$__object/parameter/state")" diff --git a/cdist/conf/type/__consul_service/explorer/conf-dir b/cdist/conf/type/__consul_service/explorer/conf-dir new file mode 100644 index 00000000..0fc9ef84 --- /dev/null +++ b/cdist/conf/type/__consul_service/explorer/conf-dir @@ -0,0 +1,15 @@ +# Determine the configuration directory used by consul. + +check_dir () { + if [ -d "$1" ]; then + printf '%s' "$1" + exit + fi +} + +check_dir '/etc/consul/conf.d' +check_dir '/etc/consul.d' +check_dir '/etc/consul' + +echo 'Could not determine consul configuration dir. Exiting.' >&2 +exit 1 diff --git a/cdist/conf/type/__consul_service/manifest b/cdist/conf/type/__consul_service/manifest index 60397db7..d16f18e0 100755 --- a/cdist/conf/type/__consul_service/manifest +++ b/cdist/conf/type/__consul_service/manifest @@ -19,7 +19,7 @@ # name="$(cat "$__object/parameter/name" 2>/dev/null || echo "$__object_id")" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="service_${name}.json" state="$(cat "$__object/parameter/state")" @@ -45,7 +45,7 @@ printf ' "name": "%s"\n' "$name" cd "$__object/parameter/" for param in *; do case "$param" in - state|name|check-interval) continue ;; + state|name|check-interval|conf-dir) continue ;; check-script) printf ' ,"check": {\n' printf ' "script": "%s"\n' "$(cat "$__object/parameter/check-script")" @@ -86,7 +86,6 @@ echo " }" # end json file echo "}" ) | \ -require="__directory${conf_dir}" \ __config_file "${conf_dir}/${conf_file}" \ --owner root --group consul --mode 640 \ --state "$state" \ diff --git a/cdist/conf/type/__consul_watch_checks/explorer/conf-dir b/cdist/conf/type/__consul_watch_checks/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_checks/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_checks/manifest b/cdist/conf/type/__consul_watch_checks/manifest index 5fdd7a74..4976b25a 100755 --- a/cdist/conf/type/__consul_watch_checks/manifest +++ b/cdist/conf/type/__consul_watch_checks/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" diff --git a/cdist/conf/type/__consul_watch_event/explorer/conf-dir b/cdist/conf/type/__consul_watch_event/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_event/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_event/manifest b/cdist/conf/type/__consul_watch_event/manifest index 61934656..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_event/manifest +++ b/cdist/conf/type/__consul_watch_event/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" diff --git a/cdist/conf/type/__consul_watch_key/explorer/conf-dir b/cdist/conf/type/__consul_watch_key/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_key/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_key/manifest b/cdist/conf/type/__consul_watch_key/manifest index 61934656..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_key/manifest +++ b/cdist/conf/type/__consul_watch_key/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" diff --git a/cdist/conf/type/__consul_watch_keyprefix/explorer/conf-dir b/cdist/conf/type/__consul_watch_keyprefix/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_keyprefix/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_keyprefix/manifest b/cdist/conf/type/__consul_watch_keyprefix/manifest index 61934656..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_keyprefix/manifest +++ b/cdist/conf/type/__consul_watch_keyprefix/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" diff --git a/cdist/conf/type/__consul_watch_nodes/explorer/conf-dir b/cdist/conf/type/__consul_watch_nodes/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_nodes/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_nodes/manifest b/cdist/conf/type/__consul_watch_nodes/manifest index 61934656..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_nodes/manifest +++ b/cdist/conf/type/__consul_watch_nodes/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" diff --git a/cdist/conf/type/__consul_watch_service/explorer/conf-dir b/cdist/conf/type/__consul_watch_service/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_service/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_service/manifest b/cdist/conf/type/__consul_watch_service/manifest index db38eb18..e8d18328 100755 --- a/cdist/conf/type/__consul_watch_service/manifest +++ b/cdist/conf/type/__consul_watch_service/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" diff --git a/cdist/conf/type/__consul_watch_services/explorer/conf-dir b/cdist/conf/type/__consul_watch_services/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_services/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_services/manifest b/cdist/conf/type/__consul_watch_services/manifest index 61934656..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_services/manifest +++ b/cdist/conf/type/__consul_watch_services/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" diff --git a/cdist/conf/type/__cron/gencode-remote b/cdist/conf/type/__cron/gencode-remote index 59398058..9debbc47 100755 --- a/cdist/conf/type/__cron/gencode-remote +++ b/cdist/conf/type/__cron/gencode-remote @@ -31,24 +31,28 @@ if [ -f "$__object/parameter/raw" ]; then elif [ -f "$__object/parameter/raw_command" ]; then entry="$command" else - minute="$(cat "$__object/parameter/minute" 2>/dev/null || echo "*")" - hour="$(cat "$__object/parameter/hour" 2>/dev/null || echo "*")" - day_of_month="$(cat "$__object/parameter/day_of_month" 2>/dev/null || echo "*")" - month="$(cat "$__object/parameter/month" 2>/dev/null || echo "*")" - day_of_week="$(cat "$__object/parameter/day_of_week" 2>/dev/null || echo "*")" + minute="$(cat "$__object/parameter/minute")" + hour="$(cat "$__object/parameter/hour")" + day_of_month="$(cat "$__object/parameter/day_of_month")" + month="$(cat "$__object/parameter/month")" + day_of_week="$(cat "$__object/parameter/day_of_week")" entry="$minute $hour $day_of_month $month $day_of_week $command # $name" fi mkdir "$__object/files" echo "$entry" > "$__object/files/entry" -if diff -q "$__object/files/entry" "$__object/explorer/entry" >/dev/null; then - state_is=present +if [ -s "$__object/explorer/entry" ]; then + if diff -q "$__object/files/entry" "$__object/explorer/entry" >/dev/null; then + state_is=present + else + state_is=modified + fi else state_is=absent fi -state_should="$(cat "$__object/parameter/state" 2>/dev/null || echo "present")" +state_should="$(cat "$__object/parameter/state")" [ "$state_is" = "$state_should" ] && exit 0 diff --git a/cdist/conf/type/__cron/manifest b/cdist/conf/type/__cron/manifest index 53973e07..e7b51863 100755 --- a/cdist/conf/type/__cron/manifest +++ b/cdist/conf/type/__cron/manifest @@ -22,3 +22,12 @@ if [ -f "$__object/parameter/raw" ] && [ -f "$__object/parameter/raw_command" ]; echo "ERROR: both raw and raw_command specified" >&2 exit 1 fi + +case "$(cat "$__object/parameter/state")" in + present) ;; + absent) ;; + + *) + echo "ERROR: unkown cron state" >&2 + exit 2 +esac diff --git a/cdist/conf/type/__cron/nonparallel b/cdist/conf/type/__cron/nonparallel new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__cron/parameter/default/day_of_month b/cdist/conf/type/__cron/parameter/default/day_of_month new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/day_of_month @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/day_of_week b/cdist/conf/type/__cron/parameter/default/day_of_week new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/day_of_week @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/hour b/cdist/conf/type/__cron/parameter/default/hour new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/hour @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/minute b/cdist/conf/type/__cron/parameter/default/minute new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/minute @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/month b/cdist/conf/type/__cron/parameter/default/month new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/month @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/state b/cdist/conf/type/__cron/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__daemontools_service/man.rst b/cdist/conf/type/__daemontools_service/man.rst index ec1d20ff..9bbbe2f8 100644 --- a/cdist/conf/type/__daemontools_service/man.rst +++ b/cdist/conf/type/__daemontools_service/man.rst @@ -40,6 +40,12 @@ run-file log-run Command to run for log consumption. Default: `multilog t ./main` +owner + User to chown to. + +group + User to chgrp to. + servicedir Directory to install into. Default: `/service` diff --git a/cdist/conf/type/__daemontools_service/manifest b/cdist/conf/type/__daemontools_service/manifest index 78bae285..8a81b5f5 100755 --- a/cdist/conf/type/__daemontools_service/manifest +++ b/cdist/conf/type/__daemontools_service/manifest @@ -9,6 +9,8 @@ servicedir=$(cat "$__object/parameter/servicedir") run=$(cat "$__object/parameter/run") runfile=$(cat "$__object/parameter/run-file") logrun=$(cat "$__object/parameter/log-run") +owner=$(cat "$__object/parameter/owner") +group=$(cat "$__object/parameter/group") svc=$(cat "$__type/explorer/svc") @@ -25,14 +27,22 @@ badusage() { [ -z "$run$runfile" ] && badusage [ -n "$run" ] && [ -n "$runfile" ] && badusage -__directory "$servicedir/$name/log/main" --parents +flags="" +if [ -n "$owner" ]; then + flags="$flags --owner $owner" +fi +if [ -n "$group" ]; then + flags="$flags --group $group" +fi + +__directory "$servicedir/$name/log/main" --parents $flags echo "$RUN_PREFIX$run" | require="__directory/$servicedir/$name/log/main" __config_file "$servicedir/$name/run" \ --onchange "svc -t '$servicedir/$name' 2>/dev/null" \ - --mode 755 \ + --mode 755 $flags \ --source "${runfile:--}" echo "$RUN_PREFIX$logrun" | require="__directory/$servicedir/$name/log/main" __config_file "$servicedir/$name/log/run" \ --onchange "svc -t '$servicedir/$name/log' 2>/dev/null" \ - --mode 755 \ + --mode 755 $flags \ --source "-" diff --git a/cdist/conf/type/__daemontools_service/parameter/default/group b/cdist/conf/type/__daemontools_service/parameter/default/group new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__daemontools_service/parameter/default/owner b/cdist/conf/type/__daemontools_service/parameter/default/owner new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__daemontools_service/parameter/optional b/cdist/conf/type/__daemontools_service/parameter/optional index 7e54985f..7c66b514 100644 --- a/cdist/conf/type/__daemontools_service/parameter/optional +++ b/cdist/conf/type/__daemontools_service/parameter/optional @@ -1,4 +1,6 @@ +group log-run +owner run run-file servicedir diff --git a/cdist/conf/type/__directory/explorer/stat b/cdist/conf/type/__directory/explorer/stat index 03d466ba..105d894f 100755 --- a/cdist/conf/type/__directory/explorer/stat +++ b/cdist/conf/type/__directory/explorer/stat @@ -1,6 +1,7 @@ #!/bin/sh # # 2013 Steven Armstrong (steven-cdist armstrong.cc) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -20,24 +21,43 @@ destination="/$__object_id" +fallback() { + # Patch the output together, manually + + ls_line=$(ls -ldn "$destination") + + uid=$(echo "$ls_line" | awk '{ print $3 }') + gid=$(echo "$ls_line" | awk '{ print $4 }') + + owner=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/passwd) + group=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/group) + + mode_text=$(echo "$ls_line" | awk '{ print $1 }') + mode=$(echo "$mode_text" | awk '{ k=0; for (i=0; i<=8; i++) k += ((substr($1, i+2, 1) ~ /[rwx]/) * 2^(8-i)); printf("%0o", k) }') + + printf 'type: %s\nowner: %d %s\ngroup: %d %s\nmode: %s %s\n' \ + "$("$__type_explorer/type")" \ + "$uid" "$owner" \ + "$gid" "$group" \ + "$mode" "$mode_text" +} + # nothing to work with, nothing we could do [ -e "$destination" ] || exit 0 -os=$("$__explorer/os") -case "$os" in +if ! command -v stat >/dev/null +then + fallback + exit +fi + +case $("$__explorer/os") in "freebsd"|"netbsd"|"openbsd"|"macosx") stat -f "type: %HT owner: %Du %Su group: %Dg %Sg mode: %Lp %Sp -" "$destination" | awk '/^type/ { print tolower($0); next; } { print; }' - ;; - alpine) - stat -c "type: %F -owner: %u %U -group: %g %G -mode: %a %A -" "$destination" +" "$destination" | awk '/^type/ { print tolower($0); next } { print }' ;; solaris) ls1="$( ls -ld "$destination" )" @@ -69,10 +89,12 @@ mode: %a %A echo "mode: $octets $( echo "$ls1" | awk '{print $1}' )" ;; *) - stat --printf="type: %F + # NOTE: Do not use --printf here as it is not supported by BusyBox stat. + # NOTE: BusyBox's stat might not support the "-c" option, in which case + # we fall through to the shell fallback. + stat -c "type: %F owner: %u %U group: %g %G -mode: %a %A -" "$destination" +mode: %a %A" "$destination" 2>/dev/null || fallback ;; esac diff --git a/cdist/conf/type/__directory/gencode-remote b/cdist/conf/type/__directory/gencode-remote index 374db47a..a1a32ea2 100755 --- a/cdist/conf/type/__directory/gencode-remote +++ b/cdist/conf/type/__directory/gencode-remote @@ -3,6 +3,7 @@ # 2011-2013 Nico Schottelius (nico-cdist at schottelius.org) # 2013 Steven Armstrong (steven-cdist armstrong.cc) # 2014 Daniel Heule (hda at sfs.biz) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -21,8 +22,8 @@ # destination="/$__object_id" -state_should="$(cat "$__object/parameter/state")" -type="$(cat "$__object/explorer/type")" +state_should=$(cat "$__object/parameter/state") +type=$(cat "$__object/explorer/type") stat_file="$__object/explorer/stat" # variable to keep track if we have to set directory attributes @@ -72,7 +73,7 @@ set_mode() { } case "$state_should" in - present) + present|exists) if [ "$type" != "directory" ]; then set_attributes=1 if [ "$type" != "none" ]; then @@ -83,6 +84,10 @@ case "$state_should" in fi echo "mkdir $mkdiropt '$destination'" echo "create" >> "$__messages_out" + elif [ "$state_should" = 'exists' ]; then + # The type is directory and --state exists. We are done and do not + # check or set the attributes. + exit 0 fi # Note: Mode - needs to happen last as a chown/chgrp can alter mode by @@ -103,6 +108,26 @@ case "$state_should" in fi done ;; + pre-exists) + case $type in + directory) + # all good + exit 0 + ;; + none) + printf 'Directory "%s" does not exist\n' "$destination" >&2 + exit 1 + ;; + file|symlink) + printf 'File "%s" exists and is a %s, but should be a directory\n' "$destination" "$type" >&2 + exit 1 + ;; + *) + printf 'File or directory "%s" is in an unknown state\n' "$destination" >&2 + exit 1 + ;; + esac + ;; absent) if [ "$type" = "directory" ]; then echo "rm -rf '$destination'" diff --git a/cdist/conf/type/__directory/man.rst b/cdist/conf/type/__directory/man.rst index 74b00afe..7755334c 100644 --- a/cdist/conf/type/__directory/man.rst +++ b/cdist/conf/type/__directory/man.rst @@ -19,7 +19,18 @@ None. OPTIONAL PARAMETERS ------------------- state - 'present' or 'absent', defaults to 'present' + 'present', 'absent', 'exists' or 'pre-exists', defaults to 'present' where: + + present + the directory exists and the given attributes are set. + absent + the directory does not exist. + exists + the directory exists, but its attributes are not altered if it already + existed. + pre-exists + check that the directory exists and is indeed a directory, but do not + create or modify it. group Group to chgrp to. @@ -36,7 +47,7 @@ BOOLEAN PARAMETERS parents Whether to create parents as well (mkdir -p behaviour). Warning: all intermediate directory permissions default - to whatever mkdir -p does. + to whatever mkdir -p does. Usually this means root:root, 0700. diff --git a/cdist/conf/type/__file/explorer/stat b/cdist/conf/type/__file/explorer/stat index 13c1c208..91c8cc84 100755 --- a/cdist/conf/type/__file/explorer/stat +++ b/cdist/conf/type/__file/explorer/stat @@ -2,6 +2,7 @@ # # 2013 Steven Armstrong (steven-cdist armstrong.cc) # 2019 Nico Schottelius (nico-cdist at schottelius.org) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -21,29 +22,54 @@ destination="/$__object_id" +fallback() { + # Fallback: Patch the output together, manually. + + ls_line=$(ls -ldn "$destination") + + uid=$(echo "$ls_line" | awk '{ print $3 }') + gid=$(echo "$ls_line" | awk '{ print $4 }') + + owner=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/passwd) + group=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/group) + + mode_text=$(echo "$ls_line" | awk '{ print $1 }') + mode=$(echo "$mode_text" | awk '{ k=0; for (i=0; i<=8; i++) k += ((substr($1, i+2, 1) ~ /[rwx]/) * 2^(8-i)); printf("%0o", k) }') + + size=$(echo "$ls_line" | awk '{ print $5 }') + links=$(echo "$ls_line" | awk '{ print $2 }') + + printf 'type: %s\nowner: %d %s\ngroup: %d %s\nmode: %s %s\nsize: %d\nlinks: %d\n' \ + "$("$__type_explorer/type")" \ + "$uid" "$owner" \ + "$gid" "$group" \ + "$mode" "$mode_text" \ + "$size" \ + "$links" +} + + # nothing to work with, nothing we could do [ -e "$destination" ] || exit 0 -os=$("$__explorer/os") -case "$os" in - "freebsd"|"netbsd"|"openbsd"|"macosx") + +if ! command -v stat >/dev/null +then + fallback + exit +fi + + +case $("$__explorer/os") +in + freebsd|netbsd|openbsd|macosx) stat -f "type: %HT owner: %Du %Su group: %Dg %Sg mode: %Lp %Sp size: %Dz links: %Dl -" "$destination" | awk '/^type/ { print tolower($0); next; } { print; }' - ;; - alpine) - # busybox stat - stat -c "type: %F -owner: %u %U -group: %g %G -mode: %a %A -size: %s -links: %h -" "$destination" +" "$destination" | awk '/^type/ { print tolower($0); next } { print }' ;; solaris) ls1="$( ls -ld "$destination" )" @@ -77,12 +103,14 @@ links: %h echo "links: $( echo "$ls1" | awk '{print $2}' )" ;; *) - stat --printf="type: %F + # NOTE: Do not use --printf here as it is not supported by BusyBox stat. + # NOTE: BusyBox's stat might not support the "-c" option, in which case + # we fall through to the shell fallback. + stat -c "type: %F owner: %u %U group: %g %G mode: %a %A size: %s -links: %h -" "$destination" - ;; +links: %h" "$destination" 2>/dev/null || fallback + ;; esac diff --git a/cdist/conf/type/__file/gencode-local b/cdist/conf/type/__file/gencode-local index fb9f9a92..231b6927 100755 --- a/cdist/conf/type/__file/gencode-local +++ b/cdist/conf/type/__file/gencode-local @@ -31,12 +31,24 @@ if [ "$state_should" = "pre-exists" ]; then exit 1 fi - if [ "$type" = "file" ]; then - exit 0 # nothing to do - else - echo "File \"$destination\" does not exist" - exit 1 - fi + case $type in + file) + # nothing to do + exit 0 + ;; + none) + printf 'File "%s" does not exist\n' "$destination" >&2 + exit 1 + ;; + directory|symlink) + printf 'File "%s" exists and is a %s, but should be a regular file\n' "$destination" "$type" >&2 + exit 1 + ;; + *) + printf 'File or directory "%s" is in an unknown state\n' "$destination" >&2 + exit 1 + ;; + esac fi upload_file= diff --git a/cdist/conf/type/__file/gencode-remote b/cdist/conf/type/__file/gencode-remote index b04c471e..815593bd 100755 --- a/cdist/conf/type/__file/gencode-remote +++ b/cdist/conf/type/__file/gencode-remote @@ -55,37 +55,41 @@ set_owner() { } set_mode() { - echo "chmod '$1' '$destination'" - echo "chmod '$1'" >> "$__messages_out" - fire_onchange=1 + echo "chmod '$1' '$destination'" + echo "chmod '$1'" >> "$__messages_out" + fire_onchange=1 } case "$state_should" in - present|exists|pre-exists) - # Note: Mode - needs to happen last as a chown/chgrp can alter mode by - # clearing S_ISUID and S_ISGID bits (see chown(2)) - for attribute in group owner mode; do - if [ -f "$__object/parameter/$attribute" ]; then - value_should="$(cat "$__object/parameter/$attribute")" + present|exists) + # Note: Mode - needs to happen last as a chown/chgrp can alter mode by + # clearing S_ISUID and S_ISGID bits (see chown(2)) + for attribute in group owner mode; do + if [ -f "$__object/parameter/$attribute" ]; then + value_should="$(cat "$__object/parameter/$attribute")" - # change 0xxx format to xxx format => same as stat returns - if [ "$attribute" = mode ]; then - value_should="$(echo "$value_should" | sed 's/^0\(...\)/\1/')" - fi - - value_is="$(get_current_value "$attribute" "$value_should")" - if [ -f "$__object/files/set-attributes" ] || [ "$value_should" != "$value_is" ]; then - "set_$attribute" "$value_should" + # change 0xxx format to xxx format => same as stat returns + if [ "$attribute" = mode ]; then + value_should="$(echo "$value_should" | sed 's/^0\(...\)/\1/')" + fi + + value_is="$(get_current_value "$attribute" "$value_should")" + if [ -f "$__object/files/set-attributes" ] || [ "$value_should" != "$value_is" ]; then + "set_$attribute" "$value_should" + fi fi + done + if [ -f "$__object/files/set-attributes" ]; then + # set-attributes is created if file is created or uploaded in gencode-local + fire_onchange=1 fi - done - if [ -f "$__object/files/set-attributes" ]; then - # set-attributes is created if file is created or uploaded in gencode-local - fire_onchange=1 - fi - ;; + pre-exists) + # pre-exists should never reach gencode-remote… + exit 1 + ;; + absent) if [ "$type" = "file" ]; then echo "rm -f '$destination'" @@ -101,7 +105,7 @@ case "$state_should" in esac if [ -f "$__object/parameter/onchange" ]; then - if [ -n "$fire_onchange" ]; then - cat "$__object/parameter/onchange" - fi + if [ -n "$fire_onchange" ]; then + cat "$__object/parameter/onchange" + fi fi diff --git a/cdist/conf/type/__install_chroot_umount/manifest b/cdist/conf/type/__install_chroot_umount/manifest new file mode 120000 index 00000000..f17af67a --- /dev/null +++ b/cdist/conf/type/__install_chroot_umount/manifest @@ -0,0 +1 @@ +../__chroot_umount/manifest \ No newline at end of file diff --git a/cdist/conf/type/__install_directory/man.rst b/cdist/conf/type/__install_directory/man.rst deleted file mode 120000 index 1ad7fa84..00000000 --- a/cdist/conf/type/__install_directory/man.rst +++ /dev/null @@ -1 +0,0 @@ -../__directory/man.rst \ No newline at end of file diff --git a/cdist/conf/type/__install_directory/man.rst b/cdist/conf/type/__install_directory/man.rst new file mode 100644 index 00000000..c402cbad --- /dev/null +++ b/cdist/conf/type/__install_directory/man.rst @@ -0,0 +1,101 @@ +cdist-type__install_directory(7) +================================ + +NAME +---- +cdist-type__install_directory - Manage a directory with install command + + +DESCRIPTION +----------- +This cdist type allows you to create or remove directories on the target. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +state + 'present' or 'absent', defaults to 'present' + +group + Group to chgrp to. + +mode + Unix permissions, suitable for chmod. + +owner + User to chown to. + + +BOOLEAN PARAMETERS +------------------ +parents + Whether to create parents as well (mkdir -p behaviour). + Warning: all intermediate directory permissions default + to whatever mkdir -p does. + + Usually this means root:root, 0700. + +recursive + If supplied the chgrp and chown call will run recursively. + This does *not* influence the behaviour of chmod. + +MESSAGES +-------- +chgrp + Changed group membership +chown + Changed owner +chmod + Changed mode +create + Empty directory was created +remove + Directory exists, but state is absent, directory will be removed by generated code. +remove non directory + Something other than a directory with the same name exists and was removed prior to create. + + +EXAMPLES +-------- + +.. code-block:: sh + + # A silly example + __install_directory /tmp/foobar + + # Remove a directory + __install_directory /tmp/foobar --state absent + + # Ensure /etc exists correctly + __install_directory /etc --owner root --group root --mode 0755 + + # Create nfs service directory, including parents + __install_directory /home/services/nfs --parents + + # Change permissions recursively + __install_directory /home/services --recursive --owner root --group root + + # Setup a temp directory + __install_directory /local --mode 1777 + + # Take it all + __install_directory /home/services/kvm --recursive --parents \ + --owner root --group root --mode 0755 --state present + + +AUTHORS +------- +Nico Schottelius + + +COPYING +------- +Copyright \(C) 2011 Nico Schottelius. 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. diff --git a/cdist/conf/type/__install_file/man.rst b/cdist/conf/type/__install_file/man.rst index c5409167..977ed77c 100644 --- a/cdist/conf/type/__install_file/man.rst +++ b/cdist/conf/type/__install_file/man.rst @@ -23,6 +23,10 @@ symlink directory replace it with the source file +One exception is that when state is pre-exists, an error is raised if +the file would have been created otherwise (e.g. it is not present or +not a regular file). + In any case, make sure that the file attributes are as specified. @@ -33,7 +37,7 @@ None. OPTIONAL PARAMETERS ------------------- state - 'present', 'absent' or 'exists', defaults to 'present' where: + 'present', 'absent', 'exists' or 'pre-exists', defaults to 'present' where: present the file is exactly the one from source @@ -41,6 +45,9 @@ state the file does not exist exists the file from source but only if it doesn't already exist + pre-exists + check that the file exists and is a regular file, but do not + create or modify it group Group to chgrp to. @@ -56,6 +63,9 @@ source If not supplied, an empty file or directory will be created. If source is '-' (dash), take what was written to stdin as the file content. +onchange + The code to run if file is modified. + MESSAGES -------- chgrp @@ -93,6 +103,8 @@ EXAMPLES __install_file /home/frodo/.bashrc --source "/etc/skel/.bashrc" \ --state exists \ --owner frodo --mode 0600 + # Check that the file is present, show an error when it is not + __install_file /etc/somefile --state pre-exists # Take file content from stdin __install_file /tmp/whatever --owner root --group root --mode 644 --source - << DONE Here goes the content for /tmp/whatever diff --git a/cdist/conf/type/__iocage_clone/gencode-remote b/cdist/conf/type/__iocage_clone/gencode-remote new file mode 100755 index 00000000..30d77099 --- /dev/null +++ b/cdist/conf/type/__iocage_clone/gencode-remote @@ -0,0 +1,152 @@ +#!/bin/sh + +state="$(cat $__object/parameter/state)" +template="$(cat $__object/parameter/template)" +ip4_addr="$(cat $__object/parameter/bridge)|$(cat $__object/parameter/ip)" +interfaces="none:none" +defaultrouter="none" +vnet="off" +jail_zfs_dataset="$(cat $__object/parameter/jail_zfs_dataset)" +devfs_ruleset="$(cat $__object/parameter/devfs_ruleset)" +allow_socket_af="$(cat $__object/parameter/allow_socket_af)" +mount_procfs="$(cat $__object/parameter/mount_procfs)" +mount_linprocfs="$(cat $__object/parameter/mount_linprocfs)" + +if [ "X$state" = "Xabsent" ]; then + cat <&2 + create_new=1 + fi +fi + +if [ \$create_new -eq 0 ]; then + if [ "off" == "\$(get_property_iocage jail_zfs "$__object_id")" ]; then + current_jail_zfs_dataset="" + else + current_jail_zfs_dataset="\$(get_property_iocage jail_zfs_dataset "$__object_id")" + fi +fi + +configure=0 +if [ \$create_new -eq 1 ]; then + configure=1 +elif [ "X$vnet" != "X\$(get_property_iocage vnet "$__object_id")" ]; then + configure=1 +elif [ "X$ip4_addr" != "X\$(get_property_iocage ip4_addr "$__object_id")" ]; then + configure=1 +elif [ "X$interfaces" != "X\$(get_property_iocage interfaces "$__object_id")" ]; then + configure=1 +elif [ "X$defaultrouter" != "X\$(get_property_iocage defaultrouter "$__object_id")" ]; then + configure=1 +elif [ "X$mount_procfs" != "X\$(get_property_iocage mount_procfs "$__object_id")" ]; then + configure=1 +elif [ "X$devfs_ruleset" != "X\$(get_property_iocage devfs_ruleset "$__object_id")" ]; then + configure=1 +elif [ "X$allow_socket_af" != "X\$(get_property_iocage allow_socket_af "$__object_id")" ]; then + configure=1 +elif [ "X$jail_zfs_dataset" != "X\$current_jail_zfs_dataset" ]; then + configure=1 +fi + +if [ \$create_new -eq 1 ]; then + echo "Creating jail $__object_id" >&2 + + iocage stop $__object_id || true + iocage destroy -f $__object_id || true + # Without VNETs, we should not need this. + # TODO(riso): Use nicer path + # /root/cdist/ioc deconfigure $__object_id + + rm -f /iocage/jails/$__object_id + + iocage clone $template tag=$__object_id + iocage set boot=on $__object_id + UUID=\$(iocage list | grep " $__object_id " | awk "{ print \\\$2; }") + rm -f /iocage/jails/$__object_id + ln -s /iocage/jails/\$UUID /iocage/jails/$__object_id +else + UUID=\$(iocage list | grep " $__object_id " | awk "{ print \\\$2; }") + echo "Jail $__object_id already exists, UUID=\$UUID" >&2 +fi + +ROOT="/iocage/jails/\$UUID/root" +FSTAB="/iocage/jails/\$UUID/fstab" +rm -f \$FSTAB.new +touch \$FSTAB.new +cat $__object/parameter/mount 2>/dev/null | \\ +while read mount; do + src=\$(echo \$mount | awk -F: "{ print \\\$1; }") + dst_rel=\$(echo \$mount | awk -F: "{ print \\\$2; }") + dst="/iocage/jails/\$UUID/root/\$dst_rel" + mkdir -p "\$dst" + echo "\$src \$dst nullfs rw 0 0" >>\$FSTAB.new +done +if [ $mount_linprocfs -eq 1 ]; then + echo "linproc /iocage/jails/\$UUID/root/compat/linux/proc linprocfs rw 0 0" >>\$FSTAB.new +fi + +fstab_changed=0 +if diff -q \$FSTAB \$FSTAB.new >/dev/null; then + # pass +else + configure=1 + fstab_changed=1 +fi + +if [ \$configure -eq 1 ]; then + echo "Configuring jail $__object_id." >&2 + iocage stop $__object_id || true + + iocage set vnet="$vnet" $__object_id + iocage set interfaces="$interfaces" $__object_id + iocage set hostname="$__object_id" $__object_id + iocage set ip4_addr="$ip4_addr" $__object_id + iocage set defaultrouter="$defaultrouter" $__object_id + iocage set mount_procfs="$mount_procfs" $__object_id + iocage set devfs_ruleset="$devfs_ruleset" $__object_id + iocage set allow_socket_af="$allow_socket_af" $__object_id + if [ -n "$jail_zfs_dataset" ]; then + iocage set jail_zfs=on $__object_id + iocage set jail_zfs_dataset="$jail_zfs_dataset" $__object_id + else + iocage set jail_zfs=off $__object_id + fi + + if [ \$fstab_changed -eq 1 ]; then + umount -afF \$FSTAB || true + mv \$FSTAB.new \$FSTAB + fi + + iocage start $__object_id || true + + # Iocage creates new mac address, but arp can have an old mac cached. + # TODO(riso): Is this true without VNETs? + arp -d -a +else + echo "Jail $__object_id is already configured." >&2 +fi +rm -f \$FSTAB.new +EOF +fi diff --git a/cdist/conf/type/__iocage_clone/manifest b/cdist/conf/type/__iocage_clone/manifest new file mode 100644 index 00000000..0684fce8 --- /dev/null +++ b/cdist/conf/type/__iocage_clone/manifest @@ -0,0 +1 @@ +__package iocage diff --git a/cdist/conf/type/__iocage_clone/parameter/default/allow_socket_af b/cdist/conf/type/__iocage_clone/parameter/default/allow_socket_af new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/default/allow_socket_af @@ -0,0 +1 @@ +0 diff --git a/cdist/conf/type/__iocage_clone/parameter/default/bridge b/cdist/conf/type/__iocage_clone/parameter/default/bridge new file mode 100644 index 00000000..092f51c8 --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/default/bridge @@ -0,0 +1 @@ +bridge0 diff --git a/cdist/conf/type/__iocage_clone/parameter/default/devfs_ruleset b/cdist/conf/type/__iocage_clone/parameter/default/devfs_ruleset new file mode 100644 index 00000000..b8626c4c --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/default/devfs_ruleset @@ -0,0 +1 @@ +4 diff --git a/cdist/conf/type/__iocage_clone/parameter/default/jail_zfs_dataset b/cdist/conf/type/__iocage_clone/parameter/default/jail_zfs_dataset new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__iocage_clone/parameter/default/mount_linprocfs b/cdist/conf/type/__iocage_clone/parameter/default/mount_linprocfs new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/default/mount_linprocfs @@ -0,0 +1 @@ +0 diff --git a/cdist/conf/type/__iocage_clone/parameter/default/mount_procfs b/cdist/conf/type/__iocage_clone/parameter/default/mount_procfs new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/default/mount_procfs @@ -0,0 +1 @@ +0 diff --git a/cdist/conf/type/__iocage_clone/parameter/default/net b/cdist/conf/type/__iocage_clone/parameter/default/net new file mode 100644 index 00000000..a45fd52c --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/default/net @@ -0,0 +1 @@ +24 diff --git a/cdist/conf/type/__iocage_clone/parameter/default/state b/cdist/conf/type/__iocage_clone/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__iocage_clone/parameter/optional b/cdist/conf/type/__iocage_clone/parameter/optional new file mode 100644 index 00000000..8ca73ed9 --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/optional @@ -0,0 +1,7 @@ +state +bridge +jail_zfs_dataset +mount_procfs +mount_linprocfs +devfs_ruleset +allow_socket_af diff --git a/cdist/conf/type/__iocage_clone/parameter/optional_multiple b/cdist/conf/type/__iocage_clone/parameter/optional_multiple new file mode 100644 index 00000000..fde64773 --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/optional_multiple @@ -0,0 +1 @@ +mount diff --git a/cdist/conf/type/__iocage_clone/parameter/required b/cdist/conf/type/__iocage_clone/parameter/required new file mode 100644 index 00000000..209d1544 --- /dev/null +++ b/cdist/conf/type/__iocage_clone/parameter/required @@ -0,0 +1,2 @@ +ip +template diff --git a/cdist/conf/type/__letsencrypt_acmetiny/gencode-remote b/cdist/conf/type/__letsencrypt_acmetiny/gencode-remote new file mode 100644 index 00000000..9243acc9 --- /dev/null +++ b/cdist/conf/type/__letsencrypt_acmetiny/gencode-remote @@ -0,0 +1,112 @@ +#!/bin/sh -e + +ACME_TINY_CERT_REQUEST_DIR="/var/acme-tiny/cert-requests" +ACME_TINY_ACCOUNT_KEY="/var/acme-tiny/account.key" +ACME_CHALLENGE_DIR="/srv/www/sites/acme/public/.well-known/acme-challenge" + +REALM="${__object_id}" +EXTRA_DOMAINS="" +if [ -f "${__object}/parameter/extra-domain" ]; then + EXTRA_DOMAINS="$(cat "${__object}/parameter/extra-domain")" +fi + +#TODO: support linux too +REALMS_DIR="/usr/local/etc/pki/realms" +REALM_DIR="${REALMS_DIR}/${REALM}" +REALM_CERT="${REALM_DIR}/default.crt" +REALM_KEY="${REALM_DIR}/default.key" +REALM_CERT_REQUEST="${ACME_TINY_CERT_REQUEST_DIR}/${REALM}.csr" +REALM_CERT_REQUEST_CNF="${ACME_TINY_CERT_REQUEST_DIR}/${REALM}.cnf" + +CSR_ALT_NAMES="" +REALM_CERT_REQUEST_CNF_LINE="" +if [ -n "${EXTRA_DOMAINS}" ]; then + CSR_ALT_NAMES="DNS:${REALM}" + for domain in ${EXTRA_DOMAINS}; do + CSR_ALT_NAMES="${CSR_ALT_NAMES},DNS:${domain}" + done + # CSR requests are executed always against .new, only after succeeding .new replaces the .cnf + REALM_CERT_REQUEST_CNF_LINE="-reqexts SAN -config '${REALM_CERT_REQUEST_CNF}.new'" +fi + +cat << EOF +if [ ! -d '${REALM_DIR}' ]; then + mkdir -p '${REALM_DIR}' +fi +if [ ! -f '${REALM_KEY}' ]; then + openssl genrsa 4096 > '${REALM_KEY}' +fi + +if [ ! -d '${ACME_TINY_CERT_REQUEST_DIR}' ]; then + mkdir '${ACME_TINY_CERT_REQUEST_DIR}' +fi + +FORCE_CSR_REGEN="" +if [ -n '${CSR_ALT_NAMES}' ]; then + # Generate new config + cat /etc/ssl/openssl.cnf > '${REALM_CERT_REQUEST_CNF}.new' + printf '[SAN]\nsubjectAltName=${CSR_ALT_NAMES}' >> '${REALM_CERT_REQUEST_CNF}.new' + # Compare to previous config if necessary + if [ -f '${REALM_CERT_REQUEST_CNF}' ]; then + CNF_DIFF=\$(diff -q '${REALM_CERT_REQUEST_CNF}' '${REALM_CERT_REQUEST_CNF}.new' || true) + if [ -n "\${CNF_DIFF}" ]; then + # Options have changed + FORCE_CSR_REGEN="YES" + else + # Since they match, we won't be using this, clean it + rm '${REALM_CERT_REQUEST_CNF}.new' + fi + else + # We never used SAN here, CSR regen needed. + FORCE_CSR_REGEN="YES" + fi +else + # We used SAN at some point, not any more + if [ -f '${REALM_CERT_REQUEST_CNF}' ]; then + rm '${REALM_CERT_REQUEST_CNF}' + FORCE_CSR_REGEN="YES" + fi +fi + +# Create or re-create when params have changed +if [ ! -f '${REALM_CERT_REQUEST}' -o -n "\${FORCE_CSR_REGEN}" ]; then + openssl req -new -sha256 -key '${REALM_KEY}' -subj '/CN=${REALM}' -out '${REALM_CERT_REQUEST}' ${REALM_CERT_REQUEST_CNF_LINE} +fi + +# Check if cert exists, and if so whether or not it's older than a month +if [ -f '${REALM_CERT}' ]; then + MODIFIED_IN_30d="\$(find '${REALM_CERT}' -mtime -30d)" + if [ -z "\${MODIFIED_IN_30d}" ]; then + # Cert is over a month old, it's fine to regenerate + FORCE_CRT_REGEN="YES" + fi +else + # This cert doesn't exist + FORCE_CRT_REGEN="YES" +fi + + +# Only request certificate when needed +# TODO: support linux too +if [ -n "\${FORCE_CSR_REGEN}" -o -n "\${FORCE_CRT_REGEN}" ]; then + doas -u acme-tiny -- acme_tiny \ + --account '${ACME_TINY_ACCOUNT_KEY}' \ + --csr '${REALM_CERT_REQUEST}' \ + --acme-dir '${ACME_CHALLENGE_DIR}' > '${REALM_CERT}.new' + + if [ -s '${REALM_CERT}.new' ]; then + mv '${REALM_CERT}.new' '${REALM_CERT}' + else + echo "Failed to generate cert for realm '${REALM}'." + exit 1 + fi +fi + +cat "${REALM_CERT}" "${REALMS_DIR}/chain.pem" > ${REALM_DIR}/fullchain.pem + +if [ -n '${REALM_CERT_REQUEST_CNF_LINE}' -a -f '${REALM_CERT_REQUEST_CNF}.new' ]; then + # CSR and cert generation succeded with a new config, put new config in-place. + # This is the last thing we do, so we try again next time if sth fails. + mv '${REALM_CERT_REQUEST_CNF}.new' '${REALM_CERT_REQUEST_CNF}' +fi +EOF diff --git a/cdist/conf/type/__letsencrypt_acmetiny/manifest b/cdist/conf/type/__letsencrypt_acmetiny/manifest new file mode 100644 index 00000000..48438abb --- /dev/null +++ b/cdist/conf/type/__letsencrypt_acmetiny/manifest @@ -0,0 +1 @@ +#__letsencrypt_acmetiny_base diff --git a/cdist/conf/type/__letsencrypt_acmetiny/nonparallel b/cdist/conf/type/__letsencrypt_acmetiny/nonparallel new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__letsencrypt_acmetiny/parameter/optional_multiple b/cdist/conf/type/__letsencrypt_acmetiny/parameter/optional_multiple new file mode 100644 index 00000000..7bfb11da --- /dev/null +++ b/cdist/conf/type/__letsencrypt_acmetiny/parameter/optional_multiple @@ -0,0 +1 @@ +extra-domain diff --git a/cdist/conf/type/__letsencrypt_acmetiny_base/gencode-remote b/cdist/conf/type/__letsencrypt_acmetiny_base/gencode-remote new file mode 100644 index 00000000..1e4174a4 --- /dev/null +++ b/cdist/conf/type/__letsencrypt_acmetiny_base/gencode-remote @@ -0,0 +1,12 @@ +#!/bin/sh -e + +ACME_HOME="/var/acme-tiny" +ACME_ACCOUNT_KEY="${ACME_HOME}/account.key" + +cat << EOF +if [ ! -f '${ACME_ACCOUNT_KEY}' ]; then + openssl genrsa 4096 > '${ACME_ACCOUNT_KEY}' + chown acme-tiny:acme-tiny '${ACME_ACCOUNT_KEY}' + chmod 640 '${ACME_ACCOUNT_KEY}' +fi +EOF diff --git a/cdist/conf/type/__letsencrypt_acmetiny_base/manifest b/cdist/conf/type/__letsencrypt_acmetiny_base/manifest new file mode 100644 index 00000000..cbedcdff --- /dev/null +++ b/cdist/conf/type/__letsencrypt_acmetiny_base/manifest @@ -0,0 +1,227 @@ +# Arguments +ACME_DOMAIN="$(cat "${__object}/parameter/acme_domain" || true)" + +if [ -z "${ACME_DOMAIN}" ]; then + ACME_DOMAIN="${__target_host}" +fi + + +# Install needed stuffz + +## TODO: consider not depending on nginx? It is... practical though. +## TODO: Maybe just move this out to a sepecial type? +__package "nginx" + +NGINX_ETC="/usr/local/etc/nginx" + +# Setup the acme-challenge snippet +require="__package/nginx" __directory "${NGINX_ETC}/snippets" --state present +require="__directory${NGINX_ETC}/snippets" __file "${NGINX_ETC}/snippets/acme-challenge.conf" \ + --mode 644 \ + --source - << EOF +# This file is managed remotely, all changes will be lost + +# This was heavily inspired by debops.org. + +# Automatic Certificate Management Environment (ACME) support. +# https://tools.ietf.org/html/draft-ietf-acme-acme-01 +# https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment + + +# Return the ACME challenge present in the server public root. +# If not found, switch to global web server root. +location ^~ /.well-known/acme-challenge/ { + default_type "text/plain"; + try_files \$uri @well-known-acme-challenge; +} + +# Return the ACME challenge present in the global server public root. +# If not present, redirect request to a specified domain. +location @well-known-acme-challenge { + root /srv/www/sites/acme/public; + default_type "text/plain"; + try_files \$uri @redirect-acme-challenge; +} + +# Redirect the ACME challenge to a different host. If a redirect loop is +# detected, return 404. +location @redirect-acme-challenge { + if (\$arg_redirect) { + return 404; + } + return 307 \$scheme://${ACME_DOMAIN}\$request_uri?redirect=yes; +} + +# Return 404 if ACME challenge well known path is accessed directly. +location = /.well-known/acme-challenge/ { + return 404; +} +EOF + +require="__package/nginx" __directory "${NGINX_ETC}/sites-enabled" --state present +require="__directory${NGINX_ETC}/sites-enabled" __file "${NGINX_ETC}/nginx.conf" \ + --mode 644 \ + --source - << EOF +# This file is managed remotely, all changes will be lost + +worker_processes 1; + +# This default error log path is compiled-in to make sure configuration parsing +# errors are logged somewhere, especially during unattended boot when stderr +# isn't normally logged anywhere. This path will be touched on every nginx +# start regardless of error log location configured here. See +# https://trac.nginx.org/nginx/ticket/147 for more info. +# +#error_log /var/log/nginx/error.log; +# + +#pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + + include mime.types; + default_type application/octet-stream; + + server_tokens off; + + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 5m; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + types_hash_max_size 2048; + gzip on; + gzip_disable "msie6"; + gzip_comp_level 5; + gzip_min_length 256; + gzip_proxied any; + gzip_vary on; + gzip_types + application/atom+xml + application/javascript + application/json + application/ld+json + application/manifest+json + application/rss+xml + application/vnd.geo+json + application/vnd.ms-fontobject + application/x-font-ttf + application/x-web-app-manifest+json + application/xhtml+xml + application/xml + font/opentype + image/bmp + image/svg+xml + image/x-icon + text/cache-manifest + text/css + text/plain + text/vcard + text/vnd.rim.location.xloc + text/vtt + text/x-component + text/x-cross-domain-policy; + + # Logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + #add_header X-Clacks-Overhead "GNU Terry Pratchett"; + + # Virtual Hosts Configs + include ${NGINX_ETC}/sites-enabled/*.conf; +} +EOF + +require="__directory${NGINX_ETC}/sites-enabled" __file "${NGINX_ETC}/sites-enabled/welcome.conf" \ + --mode 644 \ + --source - << EOF +# This file is managed remotely, all changes will be lost + +# nginx server configuration for: +# - https://welcome/ + +server { + + listen [::]:80; + + server_name welcome; + + root /srv/www/sites/welcome/public; + + include snippets/acme-challenge.conf; + + location / { + return 301 https://\$host\$request_uri; + } +} +EOF + +## TODO: this is kinda bad, don't restart every time. +## Otherwise this isn't idempotent. +require="__package/nginx" __service nginx --action onerestart +require="__package/nginx" __start_on_boot nginx + + +__package "acme-tiny" + +# Create acme-tiny user and secure home dir +ACME_TINY_HOME="/var/acme-tiny" +require="__package/acme-tiny" __user acme-tiny --system --home ${ACME_TINY_HOME} --comment "acme-tiny client" +require="__user/acme-tiny" __directory "${ACME_TINY_HOME}" --state present --mode 0750 --owner acme-tiny --group acme-tiny + +# Create ACME challenge dirs to be served by nginx +ACME_PUBLIC_DIR="/srv/www/sites/acme/public" +ACME_WELLKNOWN_DIR="${ACME_PUBLIC_DIR}/.well-known" +ACME_CHALLENGE_DIR="${ACME_WELLKNOWN_DIR}/acme-challenge" +__directory "${ACME_PUBLIC_DIR}" \ + --parents \ + --state present \ + --owner acme-tiny --group www \ + --mode 2750 # TODO: check whether this does require gid? +require="__directory${ACME_PUBLIC_DIR}" __directory "${ACME_WELLKNOWN_DIR}" \ + --state present \ + --owner acme-tiny --group www \ + --mode 0750 +require="__directory${ACME_WELLKNOWN_DIR}" __directory "${ACME_CHALLENGE_DIR}" \ + --state present \ + --owner acme-tiny --group www \ + --mode 0750 + +__package doas +DOAS_CONF="/usr/local/etc/doas.conf" +require="__package/doas" __file "${DOAS_CONF}" --mode 0640 +require="__file${DOAS_CONF}" __line "${DOAS_CONF}" \ + --regex 'root as acme-tiny' \ + --line 'permit nopass root as acme-tiny' + +# Setup CA +REALMS_DIR="/usr/local/etc/pki/realms" +__directory "${REALMS_DIR}" \ + --parents \ + --state present \ + --mode 0755 + +require="__directory${REALMS_DIR}" __file ${REALMS_DIR}/intermediate.pem \ + --mode 0644 \ + --source - << EOF +$(curl -s https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt) +EOF +require="__directory${REALMS_DIR}" __file ${REALMS_DIR}/root.pem \ + --mode 0644 \ + --source - << EOF +$(curl -s https://letsencrypt.org/certs/trustid-x3-root.pem.txt) +EOF +require="__directory${REALMS_DIR}" __file ${REALMS_DIR}/chain.pem \ + --mode 0644 \ + --source - << EOF +$(curl -s https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt) +$(curl -s https://letsencrypt.org/certs/trustid-x3-root.pem.txt) +EOF + diff --git a/cdist/conf/type/__letsencrypt_acmetiny_base/parameter/optional b/cdist/conf/type/__letsencrypt_acmetiny_base/parameter/optional new file mode 100644 index 00000000..fb20814d --- /dev/null +++ b/cdist/conf/type/__letsencrypt_acmetiny_base/parameter/optional @@ -0,0 +1 @@ +acme_domain diff --git a/cdist/conf/type/__letsencrypt_acmetiny_base/singleton b/cdist/conf/type/__letsencrypt_acmetiny_base/singleton new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__letsencrypt_cert/man.rst b/cdist/conf/type/__letsencrypt_cert/man.rst index c4ffc6bc..85eb88ea 100644 --- a/cdist/conf/type/__letsencrypt_cert/man.rst +++ b/cdist/conf/type/__letsencrypt_cert/man.rst @@ -59,13 +59,13 @@ MESSAGES -------- change - Certificte was changed. + Certificate was changed. create - Certificte was created. + Certificate was created. remove - Certificte was removed. + Certificate was removed. EXAMPLES -------- diff --git a/cdist/conf/type/__line/explorer/state b/cdist/conf/type/__line/explorer/state index 2ef252c8..e8fc3630 100755 --- a/cdist/conf/type/__line/explorer/state +++ b/cdist/conf/type/__line/explorer/state @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2018 Steven Armstrong (steven-cdist at armstrong.cc) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -18,6 +19,14 @@ # along with cdist. If not, see . # +if [ -f "$__object/parameter/file" ]; then + file=$(cat "$__object/parameter/file") +else + file="/$__object_id" +fi + +[ -f "$file" ] || exit 0 + if [ -f "$__object/parameter/before" ]; then position="before" elif [ -f "$__object/parameter/after" ]; then @@ -33,63 +42,56 @@ else needle="line" fi -if [ -f "$__object/parameter/file" ]; then - file="$(cat "$__object/parameter/file")" -else - file="/$__object_id" -fi - -if [ ! -f "$file" ]; then - echo "file_missing" - exit 0 -fi - awk -v position="$position" -v needle="$needle" ' function _find(_text, _pattern) { if (needle == "regex") { return match(_text, _pattern) } else { - return index(_text, _pattern) + return index(_text, _pattern) == 1 } } BEGIN { getline anchor < (ENVIRON["__object"] "/parameter/" position) getline pattern < (ENVIRON["__object"] "/parameter/" needle) - state = "absent" + + found_line = 0 + correct_pos = (position != "after" && position != "before") } { if (position == "after") { if (match($0, anchor)) { getline if (_find($0, pattern)) { - state = "present" + found_line++ + correct_pos = 1 + exit 0 } - else { - state = "wrongposition" - } - exit 0 + } else if (_find($0, pattern)) { + found_line++ } - } - else if (position == "before") { + } else if (position == "before") { if (_find($0, pattern)) { + found_line++ getline if (match($0, anchor)) { - state = "present" + correct_pos = 1 + exit 0 } - else { - state = "wrongposition" - } - exit 0 } - } - else { + } else { if (_find($0, pattern)) { - state = "present" + found_line++ exit 0 } } } END { - print state + if (found_line && correct_pos) { + print "present" + } else if (found_line) { + print "wrongposition" + } else { + print "absent" + } } ' "$file" diff --git a/cdist/conf/type/__line/gencode-remote b/cdist/conf/type/__line/gencode-remote index 03e90c1b..88cae68b 100755 --- a/cdist/conf/type/__line/gencode-remote +++ b/cdist/conf/type/__line/gencode-remote @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2018 Steven Armstrong (steven-cdist at armstrong.cc) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -23,9 +24,20 @@ if [ -f "$__object/parameter/before" ] && [ -f "$__object/parameter/after" ]; th exit 1 fi +if [ -f "$__object/parameter/file" ]; then + file="$(cat "$__object/parameter/file")" +else + file="/$__object_id" +fi + state_should="$(cat "$__object/parameter/state")" state_is="$(cat "$__object/explorer/state")" +if [ -z "$state_is" ]; then + printf 'The file "%s" is missing. Please create it before using %s on it.\n' "$file" "${__type##*/}" >&2 + exit 1 +fi + if [ "$state_should" = "$state_is" ]; then # nothing to do exit 0 @@ -46,12 +58,6 @@ else needle="line" fi -if [ -f "$__object/parameter/file" ]; then - file="$(cat "$__object/parameter/file")" -else - file="/$__object_id" -fi - add=0 remove=0 case "$state_should" in @@ -104,10 +110,12 @@ BEGIN { if (anchor && match(\$0, anchor)) { if (position == "before") { print line + add = 0 print } else if (position == "after") { print print line + add = 0 } next } @@ -115,7 +123,7 @@ BEGIN { print } END { - if (add && position == "end") { + if (add) { print line } } diff --git a/cdist/conf/type/__motd/gencode-remote b/cdist/conf/type/__motd/gencode-remote index bc842cc8..738ea834 100755 --- a/cdist/conf/type/__motd/gencode-remote +++ b/cdist/conf/type/__motd/gencode-remote @@ -29,7 +29,18 @@ case "$os" in echo "uname -snrvm > /var/run/motd" echo "cat /etc/motd.tail >> /var/run/motd" ;; + freebsd) + # FreeBSD only updates /etc/motd on boot, + # as seen in /etc/rc.d/motd + echo "uname -sri > /etc/motd" + echo "cat /etc/motd.template >> /etc/motd" + # FreeBSD 13 starts treating motd slightly different from previous + # versions this ensures hosts have the expected config. + echo "rm /etc/motd.template || true" + echo "service motd start" + ;; *) + # Other OS tend to treat /etc/motd statically exit 0 ;; esac diff --git a/cdist/conf/type/__motd/man.rst b/cdist/conf/type/__motd/man.rst index 17369684..a567dc80 100644 --- a/cdist/conf/type/__motd/man.rst +++ b/cdist/conf/type/__motd/man.rst @@ -10,6 +10,13 @@ DESCRIPTION ----------- This cdist type allows you to easily setup /etc/motd. +.. note:: + In some OS, motd is a bit special, check `motd(5)`. + Currently Debian, Devuan, Ubuntu and FreeBSD are taken into account. + If your OS of choice does something besides /etc/motd, check the source + and contribute support for it. + Otherwise it will likely just work. + REQUIRED PARAMETERS ------------------- @@ -20,6 +27,7 @@ OPTIONAL PARAMETERS ------------------- source If supplied, copy this file from the host running cdist to the target. + If source is '-' (dash), take what was written to stdin as the file content. If not supplied, a default message will be placed onto the target. @@ -34,6 +42,15 @@ EXAMPLES # Supply source file from a different type __motd --source "$__type/files/my-motd" + # Supply source from stdin + __motd --source "-" < COPYING ------- -Copyright \(C) 2011 Nico Schottelius. You can redistribute it +Copyright \(C) 2020 Nico Schottelius. 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. diff --git a/cdist/conf/type/__motd/manifest b/cdist/conf/type/__motd/manifest index cd741cf4..ded734d7 100755 --- a/cdist/conf/type/__motd/manifest +++ b/cdist/conf/type/__motd/manifest @@ -34,9 +34,17 @@ os=$(cat "$__global/explorer/os") case "$os" in debian|ubuntu|devuan) + # Debian-based systems use /etc/motd.tail as a template destination=/etc/motd.tail ;; + freebsd) + # FreeBSD uses motd.template to prepend system information on boot + # (this actually only applies starting with version 13, + # but we fix that for whatever version in gencode-remote) + destination=/etc/motd.template + ;; *) + # Most UNIX systems, including other Linux and OpenBSD just use /etc/motd destination=/etc/motd ;; esac diff --git a/cdist/conf/type/__mysql_database/explorer/state b/cdist/conf/type/__mysql_database/explorer/state new file mode 100755 index 00000000..79858695 --- /dev/null +++ b/cdist/conf/type/__mysql_database/explorer/state @@ -0,0 +1,33 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +if [ -f "$__object/parameter/name" ] +then + name="$( cat "$__object/parameter/name" )" +else + name="$__object_id" +fi + +if [ -n "$( mysql -B -N -e "show databases like '$name'" )" ] +then + echo 'present' +else + echo 'absent' +fi diff --git a/cdist/conf/type/__mysql_database/gencode-remote b/cdist/conf/type/__mysql_database/gencode-remote index 23e51b05..1bdb2b11 100755 --- a/cdist/conf/type/__mysql_database/gencode-remote +++ b/cdist/conf/type/__mysql_database/gencode-remote @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2012 Benedikt Koeppel (code@benediktkoeppel.ch) +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) # # This file is part of cdist. # @@ -17,38 +17,30 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -# if --database was specified -if [ -f "$__object/parameter/name" ]; then - database="$(cat "$__object/parameter/name")" -else # otherwise use the object id as database name - database="$__object_id" +state_is="$( cat "$__object/explorer/state" )" + +state_should="$( cat "$__object/parameter/state" )" + +if [ "$state_is" = "$state_should" ] +then + exit 0 fi -cat <<-EOFF -mysql -u root <<-EOF - CREATE DATABASE IF NOT EXISTS $database -EOF -EOFF - -# if --user was specified -if [ -f "$__object/parameter/user" ]; then - user="$(cat "$__object/parameter/user")" - - # if --password was specified - if [ -f "$__object/parameter/password" ]; then - password="$(cat "$__object/parameter/password")" - cat <<-EOFF - mysql -u root <<-EOF - GRANT ALL PRIVILEGES ON $database.* to '$user'@'localhost' IDENTIFIED BY '$password'; -EOF -EOFF - else - cat <<-EOFF - mysql -u root <<-EOF - GRANT ALL PRIVILEGES ON $database.* to '$user'@'localhost'; -EOF -EOFF - fi +if [ -f "$__object/parameter/name" ] +then + name="$( cat "$__object/parameter/name" )" +else + name="$__object_id" fi + +case "$state_should" in + present) + echo "mysql -e 'create database \`$name\`'" + echo "create database $name" >> "$__messages_out" + ;; + absent) + echo "mysql -e 'drop database \`$name\`'" + echo "drop database $name" >> "$__messages_out" + ;; +esac diff --git a/cdist/conf/type/__mysql_database/man.rst b/cdist/conf/type/__mysql_database/man.rst index 1e245a08..b3b56b5f 100644 --- a/cdist/conf/type/__mysql_database/man.rst +++ b/cdist/conf/type/__mysql_database/man.rst @@ -8,24 +8,24 @@ cdist-type__mysql_database - Manage a MySQL database DESCRIPTION ----------- -This cdist type allows you to install a MySQL database. +Create MySQL database and optionally user with all privileges. -REQUIRED PARAMETERS -------------------- -None. OPTIONAL PARAMETERS ------------------- name - The name of the database to install - defaults to the object id + Name of database. Defaults to object id. user - A user that should have access to the database + Create user and give all privileges to database. password - The password for the user who manages the database + Password for user. + +state + Defaults to present. + If absent and user is also set, both will be removed (with privileges). EXAMPLES @@ -33,17 +33,23 @@ EXAMPLES .. code-block:: sh - __mysql_database "cdist" --name "cdist" --user "myuser" --password "mypwd" + # just create database + __mysql_database foo + + # create database with respective user with all privileges to database + __mysql_database bar \ + --user name \ + --password secret AUTHORS ------- -Benedikt Koeppel +Ander Punnar COPYING ------- -Copyright \(C) 2012 Benedikt Koeppel. 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. +Copyright \(C) 2020 Ander Punnar. 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. diff --git a/cdist/conf/type/__mysql_database/manifest b/cdist/conf/type/__mysql_database/manifest new file mode 100755 index 00000000..a3c9ed5d --- /dev/null +++ b/cdist/conf/type/__mysql_database/manifest @@ -0,0 +1,52 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +if [ -f "$__object/parameter/user" ] +then + user="$( cat "$__object/parameter/user" )" +fi + +if [ -f "$__object/parameter/password" ] +then + password="$( cat "$__object/parameter/password" )" +fi + +if [ -n "$user" ] && [ -n "$password" ] +then + if [ -f "$__object/parameter/name" ] + then + database="$( cat "$__object/parameter/name" )" + else + database="$__object_id" + fi + + state_should="$( cat "$__object/parameter/state" )" + + __mysql_user "$user" \ + --password "$password" \ + --state "$state_should" + + # removing user should remove all user's privileges + require="__mysql_user/$user" \ + __mysql_privileges "$database/$user" \ + --database "$database" \ + --user "$user" \ + --state "$state_should" +fi diff --git a/cdist/conf/type/__mysql_database/parameter/default/state b/cdist/conf/type/__mysql_database/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__mysql_database/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__mysql_database/parameter/optional b/cdist/conf/type/__mysql_database/parameter/optional index 756afee7..6c0b1e85 100644 --- a/cdist/conf/type/__mysql_database/parameter/optional +++ b/cdist/conf/type/__mysql_database/parameter/optional @@ -1,3 +1,4 @@ name user password +state diff --git a/cdist/conf/type/__mysql_privileges/explorer/state b/cdist/conf/type/__mysql_privileges/explorer/state new file mode 100755 index 00000000..4f13a70c --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/explorer/state @@ -0,0 +1,40 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +privileges="$( cat "$__object/parameter/privileges" )" + +database="$( cat "$__object/parameter/database" )" + +table="$( cat "$__object/parameter/table" )" + +user="$( cat "$__object/parameter/user" )" + +host="$( cat "$__object/parameter/host" )" + +check_privileges="$( + mysql -B -N -e "show grants for '$user'@'$host'" \ + | grep -Ei "^grant $privileges on .$database.\..?$table.? to " || true )" + +if [ -n "$check_privileges" ] +then + echo 'present' +else + echo 'absent' +fi diff --git a/cdist/conf/type/__mysql_privileges/gencode-remote b/cdist/conf/type/__mysql_privileges/gencode-remote new file mode 100755 index 00000000..0656699f --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/gencode-remote @@ -0,0 +1,55 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +state_is="$( cat "$__object/explorer/state" )" + +state_should="$( cat "$__object/parameter/state" )" + +if [ "$state_is" = "$state_should" ] +then + exit 0 +fi + +privileges="$( cat "$__object/parameter/privileges" )" + +database="$( cat "$__object/parameter/database" )" + +table="$( cat "$__object/parameter/table" )" + +user="$( cat "$__object/parameter/user" )" + +host="$( cat "$__object/parameter/host" )" + +if [ "$table" != '*' ] +then + # shellcheck disable=SC2016 + table="$( printf '`%s`' "$table" )" +fi + +case "$state_should" in + present) + echo "mysql -e 'grant $privileges on \`$database\`.$table to \`$user\`@\`$host\`'" + echo "grant $privileges on $database.$table to $user@$host" >> "$__messages_out" + ;; + absent) + echo "mysql -e 'revoke $privileges on \`$database\`.$table from \`$user\`@\`$host\`'" + echo "revoke $privileges on $database.$table from $user@$host" >> "$__messages_out" + ;; +esac diff --git a/cdist/conf/type/__mysql_privileges/man.rst b/cdist/conf/type/__mysql_privileges/man.rst new file mode 100644 index 00000000..b72c9eba --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/man.rst @@ -0,0 +1,57 @@ +cdist-type__mysql_privileges(7) +=============================== + +NAME +---- +cdist-type__mysql_privileges - Manage MySQL privileges + + +DESCRIPTION +----------- + +Grant and revoke privileges of MySQL user. + + +REQUIRED PARAMETERS +------------------- +database + Name of database. + +user + Name of user. + + +OPTIONAL PARAMETERS +------------------- +privileges + Defaults to "all". + +table + Defaults to "*". + +host + Defaults to localhost. + +state + "present" grants and "absent" revokes. Defaults to present. + + +EXAMPLES +-------- + +.. code-block:: sh + + __mysql_privileges user-to-db --database db --user user + + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2020 Ander Punnar. 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. diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/host b/cdist/conf/type/__mysql_privileges/parameter/default/host new file mode 100644 index 00000000..2fbb50c4 --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/default/host @@ -0,0 +1 @@ +localhost diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/privileges b/cdist/conf/type/__mysql_privileges/parameter/default/privileges new file mode 100644 index 00000000..5472efad --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/default/privileges @@ -0,0 +1 @@ +all privileges diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/state b/cdist/conf/type/__mysql_privileges/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/table b/cdist/conf/type/__mysql_privileges/parameter/default/table new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/default/table @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__mysql_privileges/parameter/optional b/cdist/conf/type/__mysql_privileges/parameter/optional new file mode 100644 index 00000000..d4ed5bc5 --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/optional @@ -0,0 +1,4 @@ +privileges +table +host +state diff --git a/cdist/conf/type/__mysql_privileges/parameter/required b/cdist/conf/type/__mysql_privileges/parameter/required new file mode 100644 index 00000000..152b4a1e --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/required @@ -0,0 +1,2 @@ +database +user diff --git a/cdist/conf/type/__mysql_user/explorer/state b/cdist/conf/type/__mysql_user/explorer/state new file mode 100755 index 00000000..6817ee9d --- /dev/null +++ b/cdist/conf/type/__mysql_user/explorer/state @@ -0,0 +1,54 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +if [ -f "$__object/parameter/name" ] +then + name="$( cat "$__object/parameter/name" )" +else + name="$__object_id" +fi + +if [ -f "$__object/parameter/password" ] +then + password="$( cat "$__object/parameter/password" )" +else + password='' +fi + +host="$( cat "$__object/parameter/host" )" + +check_user="$( mysql -B -N -e "select user from mysql.user where user = '$name' and host = '$host'" )" + +if [ -n "$check_user" ] +then + if [ -n "$password" ] + then + check_password="$( mysql -B -N -e "select user from mysql.user where user = '$name' and host = '$host' and password = password( '$password' )" )" + fi + + if [ -n "$password" ] && [ -z "$check_password" ] + then + echo 'change-password' + else + echo 'present' + fi +else + echo 'absent' +fi diff --git a/cdist/conf/type/__mysql_user/gencode-remote b/cdist/conf/type/__mysql_user/gencode-remote new file mode 100755 index 00000000..5f13bc87 --- /dev/null +++ b/cdist/conf/type/__mysql_user/gencode-remote @@ -0,0 +1,68 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +state_is="$( cat "$__object/explorer/state" )" + +state_should="$( cat "$__object/parameter/state" )" + +if [ "$state_is" = "$state_should" ] +then + exit 0 +fi + +if [ -f "$__object/parameter/name" ] +then + name="$( cat "$__object/parameter/name" )" +else + name="$__object_id" +fi + +host="$( cat "$__object/parameter/host" )" + +if [ -f "$__object/parameter/password" ] +then + password="$( cat "$__object/parameter/password" )" +else + if [ "$state_should" = 'present' ] + then + echo '--password needed' >&2 + exit 1 + else + password='' + fi +fi + +if [ "$state_is" = 'absent' ] && [ "$state_should" = 'present' ] +then + echo "mysql -e 'create user \`$name\`@\`$host\` identified by \"$password\"'" + echo "create user $name@$host" >> "$__messages_out" + +elif [ "$state_is" != 'absent' ] && [ "$state_should" = 'absent' ] +then + echo "mysql -e 'drop user \`$name\`@\`$host\`'" + echo "drop user $name@$host" >> "$__messages_out" + +elif [ "$state_is" = 'change-password' ] +then + # this only works with MySQL 5.7.6 and later or MariaDB 10.1.20 and later + echo "mysql -e 'alter user \`$name\`@\`$host\` identified by \"$password\"'" + echo "mysql -e 'flush privileges'" + echo "change password $name@$host" >> "$__messages_out" +fi diff --git a/cdist/conf/type/__mysql_user/man.rst b/cdist/conf/type/__mysql_user/man.rst new file mode 100644 index 00000000..c2b222d5 --- /dev/null +++ b/cdist/conf/type/__mysql_user/man.rst @@ -0,0 +1,48 @@ +cdist-type__mysql_user(7) +========================= + +NAME +---- +cdist-type__mysql_user - Manage a MySQL user + + +DESCRIPTION +----------- + +Create MySQL user or change password for the user. + + +OPTIONAL PARAMETERS +------------------- +name + Name of user. Defaults to object id. + +host + Host of user. Defaults to localhost. + +password + Password of user. + +state + Defaults to present. + + +EXAMPLES +-------- + +.. code-block:: sh + + __mysql_user user --password secret + + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2020 Ander Punnar. 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. diff --git a/cdist/conf/type/__mysql_user/parameter/default/host b/cdist/conf/type/__mysql_user/parameter/default/host new file mode 100644 index 00000000..2fbb50c4 --- /dev/null +++ b/cdist/conf/type/__mysql_user/parameter/default/host @@ -0,0 +1 @@ +localhost diff --git a/cdist/conf/type/__mysql_user/parameter/default/state b/cdist/conf/type/__mysql_user/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__mysql_user/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__mysql_user/parameter/optional b/cdist/conf/type/__mysql_user/parameter/optional new file mode 100644 index 00000000..a286266c --- /dev/null +++ b/cdist/conf/type/__mysql_user/parameter/optional @@ -0,0 +1,4 @@ +name +host +password +state diff --git a/cdist/conf/type/__openldap_server/gencode-remote b/cdist/conf/type/__openldap_server/gencode-remote new file mode 100644 index 00000000..b1e98f8c --- /dev/null +++ b/cdist/conf/type/__openldap_server/gencode-remote @@ -0,0 +1,44 @@ +#!/bin/sh + +manager_dn=$(cat "${__object}/parameter/manager-dn") +manager_password=$(cat "${__object}/parameter/manager-password") +description=$(cat "${__object}/parameter/description") +suffix=$(cat "${__object}/parameter/suffix") +suffix_dc=$(printf "%s" "${suffix}" | awk -F',' '{print $1}' | awk -F'=' '{print $2}') + +SLAPD_IPC=$(tr '\n' ' ' < "${__object}/parameter/slapd-url" | awk '{ print $1}') + +cat <&1 > /dev/null; then + # Already exists, use ldapmodify + ldapmodify -xZ -D "${manager_dn}" -w "${manager_password}" -H '${SLAPD_IPC}' < +Evilham + + +COPYING +------- +Copyright \(C) 2020 ungleich glarus ag. 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. diff --git a/cdist/conf/type/__openldap_server/manifest b/cdist/conf/type/__openldap_server/manifest new file mode 100644 index 00000000..84ba176f --- /dev/null +++ b/cdist/conf/type/__openldap_server/manifest @@ -0,0 +1,267 @@ +#!/bin/sh + +name="${__target_host}" +manager_dn=$(cat "${__object}/parameter/manager-dn") +manager_password_hash=$(cat "${__object}/parameter/manager-password-hash") +serverid=$(cat "${__object}/parameter/serverid") +suffix=$(cat "${__object}/parameter/suffix") +slapd_modules=$(cat "${__object}/parameter/module" 2>/dev/null || true) +schemas=$(cat "${__object}/parameter/schema") +slapd_urls=$(tr '\n' ' ' < "${__object}/parameter/slapd-url") +tls_cipher_suite=$(cat "${__object}/parameter/tls-cipher-suite" 2>/dev/null || true) +extra_config=$(cat "${__object}/parameter/extra-config" || true) + + +os="$(cat "${__global}/explorer/os")" + +# Setup OS-dependent vars +CONF_OWNER="root" +CONF_GROUP="root" +case "${os}" in + freebsd) + PKGS="openldap-server" + ETC="/usr/local/etc" + SLAPD_DIR="/usr/local/etc/openldap" + SLAPD_DATA_DIR="/var/db/openldap-data" + SLAPD_RUN_DIR="/var/run/openldap" + SLAPD_MODULE_PATH="/usr/local/libexec/openldap" + if [ -z "${slapd_modules}" ]; then + # It looks like ppolicy and syncprov must be compiled + slapd_modules="back_mdb back_monitor" + fi + CONF_OWNER="ldap" + CONF_GROUP="ldap" + if [ -z "${tls_cipher_suite}" ]; then + # TODO: research default for FreeBSD. 'NORMAL' appears to not work + tls_cipher_suite="HIGH:MEDIUM:+SSLv2" + fi + ;; + debian|ubuntu|devuan) + PKGS="slapd ldap-utils" + ETC="/etc" + SLAPD_DIR="/etc/ldap" + SLAPD_DATA_DIR="/var/lib/ldap" + SLAPD_RUN_DIR="/var/run/slapd" + SLAPD_MODULE_PATH="/usr/lib/ldap" + if [ -z "${slapd_modules}" ]; then + slapd_modules="back_mdb ppolicy syncprov back_monitor" + fi + if [ -z "${tls_cipher_suite}" ]; then + tls_cipher_suite="NORMAL" + fi + ;; + *) + echo "Don't know the openldap defaults for: $os" >&2 + exit 1 + ;; +esac + +PKG_MAIN=$(echo "${PKGS}" | awk '{print $1;}') + + +# Determine if __letsencrypt_cert is to be used and setup vars accordingly +if [ -f "${__object}/parameter/tls-cert" ]; then + tls_cert=$(cat "${__object}/parameter/tls-cert") + + if [ ! -f "${__object}/parameter/tls-privkey" ]; then + echo "When tls-cert is defined, tls-privkey is also required." >&2 + exit 1 + fi + tls_privkey=$(cat "${__object}/parameter/tls-privkey") + + if [ ! -f "${__object}/parameter/tls-ca" ]; then + echo "When tls-cert is defined, tls-ca is also required." >&2 + exit 1 + fi + tls_ca=$(cat "${__object}/parameter/tls-ca") + + _skip_letsencrypt_cert="YES" +else + if [ ! -f "${__object}/parameter/admin-email" ]; then + echo "When using __letsencrypt_cert, admin-email is also required." >&2 + exit 1 + fi + admin_email=$(cat "${__object}/parameter/admin-email") + + tls_cert="${SLAPD_DIR}/sasl2/cert.pem" + tls_privkey="${SLAPD_DIR}/sasl2/privkey.pem" + tls_ca="${SLAPD_DIR}/sasl2/chain.pem" +fi + +mkdir "${__object}/files" +ldapconf="${__object}/files/ldapconf" + +replication="" +if [ -f "${__object}/parameter/replicate" ]; then + replication=yes + + if [ ! -f "${__object}/parameter/syncrepl-searchbase" ]; then + echo "Requiring the searchbase for replication" >&2 + exit 1 + fi + syncrepl_searchbase=$(cat "${__object}/parameter/syncrepl-searchbase") + + if [ ! -f "${__object}/parameter/syncrepl-credentials" ]; then + echo "Requiring credentials for replication" >&2 + exit 1 + fi + + syncrepl_credentials=$(cat "${__object}/parameter/syncrepl-credentials") + + if [ ! -f "${__object}/parameter/syncrepl-host" ]; then + echo "Requiring host(s) for replication" >&2 + exit 1 + fi + syncrepl_hosts=$(cat "${__object}/parameter/syncrepl-host") + +fi + +# Install required packages +for pkg in ${PKGS}; do + __package "${pkg}" +done + + +require="__package/${PKG_MAIN}" __start_on_boot slapd + +# Setup -h flag for the listeners. See man slapd (-h flag). +case "${os}" in + freebsd) + require="__start_on_boot/slapd" __key_value \ + --file "/etc/rc.conf" \ + --key "slapd_flags" \ + --value "\"-h '${slapd_urls}'\"" \ + --delimiter "=" \ + --comment "# LDAP Listener URLs" \ + "${__target_host}__slapd_flags" + ;; + debian|ubuntu|devuan) + require="__package/${PKG_MAIN}" __line rm_slapd_conf \ + --file ${ETC}/default/slapd \ + --regex 'SLAPD_CONF=.*' \ + --state absent + + require="__package/${PKG_MAIN}" __line rm_slapd_services \ + --file ${ETC}/default/slapd \ + --regex 'SLAPD_SERVICES=.*' \ + --state absent + + require="__line/rm_slapd_conf" __line add_slapd_conf \ + --file ${ETC}/default/slapd \ + --line "SLAPD_CONF=${SLAPD_DIR}/slapd.conf" \ + --state present + + require="__line/rm_slapd_services" __line add_slapd_services \ + --file ${ETC}/default/slapd \ + --line "SLAPD_SERVICES=\"${slapd_urls}\"" \ + --state present + ;; + *) + # Nothing to do here, move on. + ;; +esac + + +if [ -z "${_skip_letsencrypt_cert}" ]; then + if [ -f "${__object}/parameter/staging" ]; then + staging="--staging" + else + staging="" + fi + + # shellcheck disable=SC2086 + __letsencrypt_cert "${name}" --admin-email "${admin_email}" \ + --renew-hook "cp ${ETC}/letsencrypt/live/${name}/*.pem ${SLAPD_DIR}/sasl2 && chown -R openldap:openldap ${SLAPD_DIR}/sasl2 && service slapd restart" \ + --automatic-renewal ${staging} +fi + +require="__package/${PKG_MAIN}" __directory ${SLAPD_DIR}/slapd.d --state absent + +if [ -z "${_skip_letsencrypt_cert}" ]; then + require="__package/${PKG_MAIN} __letsencrypt_cert/${name}" \ + __file ${SLAPD_DIR}/slapd.conf --owner ${CONF_OWNER} --group ${CONF_GROUP} --mode 644 \ + --source "${ldapconf}" +else + require="__package/${PKG_MAIN}" \ + __file ${SLAPD_DIR}/slapd.conf --owner ${CONF_OWNER} --group ${CONF_GROUP} --mode 644 \ + --source "${ldapconf}" +fi + +# Start slapd.conf +cat << EOF > "${ldapconf}" +pidfile ${SLAPD_RUN_DIR}/slapd.pid +argsfile ${SLAPD_RUN_DIR}/slapd.args + +TLSCipherSuite ${tls_cipher_suite} +TLSCertificateFile ${tls_cert} +TLSCertificateKeyFile ${tls_privkey} +TLSCACertificateFile ${tls_ca} + +disallow bind_anon +require bind +security tls=1 +EOF + +# Add specified schemas +for schema in ${schemas}; do + echo "include ${SLAPD_DIR}/schema/${schema}.schema" >> "${ldapconf}" +done + +# Add specified modules +echo "modulepath ${SLAPD_MODULE_PATH}" >> "${ldapconf}" +for module in ${slapd_modules}; do + echo "moduleload ${module}.la" >> "${ldapconf}" +done + +# Rest of the config +cat << EOF >> "${ldapconf}" +loglevel 1024 + +database mdb +maxsize 1073741824 + +suffix "${suffix}" +directory ${SLAPD_DATA_DIR} +rootdn "${manager_dn}" +rootpw "${manager_password_hash}" + +index objectClass eq,pres +index ou,cn,mail,surname,givenname eq,pres,sub +index uidNumber,gidNumber,loginShell eq,pres +index uid,memberUid eq,pres,sub +index nisMapName,nisMapEntry eq,pres,sub +index entryCSN,entryUUID eq + +${extra_config} + +serverid ${serverid} +EOF + +# Setup replication +if [ "${replication}" ]; then + rid=1; + for syncrepl in ${syncrepl_hosts}; do + cat <> "${ldapconf}" +syncrepl rid=${rid} + provider=ldap://${syncrepl} + bindmethod=simple + starttls=yes + binddn="${manager_dn}" + credentials=${syncrepl_credentials} + searchbase="${syncrepl_searchbase}" + type=refreshAndPersist + retry="5 + 5 +" + interval=00:00:00:05 +EOF + rid=$((rid + 1)) + done + cat <> "${ldapconf}" +mirrormode true +overlay syncprov +syncprov-checkpoint 100 5 +syncprov-sessionlog 100 + +database monitor +limits dn.exact="${manager_dn}" time=unlimited size=unlimited +EOF +fi diff --git a/cdist/conf/type/__openldap_server/parameter/boolean b/cdist/conf/type/__openldap_server/parameter/boolean new file mode 100644 index 00000000..45056fe9 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/boolean @@ -0,0 +1,2 @@ +staging +replicate diff --git a/cdist/conf/type/__openldap_server/parameter/default/description b/cdist/conf/type/__openldap_server/parameter/default/description new file mode 100644 index 00000000..6d8e37e1 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/default/description @@ -0,0 +1 @@ +Managed by cdist, do not edit manually. diff --git a/cdist/conf/type/__openldap_server/parameter/default/schema b/cdist/conf/type/__openldap_server/parameter/default/schema new file mode 100644 index 00000000..825bdb15 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/default/schema @@ -0,0 +1,12 @@ +corba +core +cosine +duaconf +dyngroup +inetorgperson +java +misc +nis +openldap +ppolicy +collective diff --git a/cdist/conf/type/__openldap_server/parameter/optional b/cdist/conf/type/__openldap_server/parameter/optional new file mode 100644 index 00000000..71c64659 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/optional @@ -0,0 +1,9 @@ +description +syncrepl-credentials +syncrepl-searchbase +admin-email +tls-cipher-suite +tls-cert +tls-privkey +tls-ca +extra-config diff --git a/cdist/conf/type/__openldap_server/parameter/optional_multiple b/cdist/conf/type/__openldap_server/parameter/optional_multiple new file mode 100644 index 00000000..52a83d5c --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/optional_multiple @@ -0,0 +1,3 @@ +syncrepl-host +module +schema diff --git a/cdist/conf/type/__openldap_server/parameter/required b/cdist/conf/type/__openldap_server/parameter/required new file mode 100644 index 00000000..ff58158d --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/required @@ -0,0 +1,5 @@ +manager-dn +manager-password +manager-password-hash +serverid +suffix diff --git a/cdist/conf/type/__openldap_server/parameter/required_multiple b/cdist/conf/type/__openldap_server/parameter/required_multiple new file mode 100644 index 00000000..848b8dc2 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/required_multiple @@ -0,0 +1 @@ +slapd-url \ No newline at end of file diff --git a/cdist/conf/type/__openldap_server/singleton b/cdist/conf/type/__openldap_server/singleton new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__package_apt/gencode-remote b/cdist/conf/type/__package_apt/gencode-remote index 699eb0c9..e02564a2 100755 --- a/cdist/conf/type/__package_apt/gencode-remote +++ b/cdist/conf/type/__package_apt/gencode-remote @@ -74,6 +74,14 @@ fi case "$state_should" in present) + # following is bit ugly, but important hack. + # due to how cdist config run works, there isn't + # currently better way to do it :( + cat << EOF +if [ ! -f /var/cache/apt/pkgcache.bin ] || [ "\$( stat --format %Y /var/cache/apt/pkgcache.bin )" -lt "\$( date +%s -d '-1 day' )" ] +then echo apt-get update > /dev/null 2>&1 || true +fi +EOF if [ -n "$version" ]; then name="${name}=${version}" fi diff --git a/cdist/conf/type/__package_apt/man.rst b/cdist/conf/type/__package_apt/man.rst index a3a70d91..a1691eac 100644 --- a/cdist/conf/type/__package_apt/man.rst +++ b/cdist/conf/type/__package_apt/man.rst @@ -11,6 +11,9 @@ DESCRIPTION apt-get is usually used on Debian and variants (like Ubuntu) to manage packages. +This type will also update package index, if it is older +than one day, to avoid missing package error messages. + REQUIRED PARAMETERS ------------------- diff --git a/cdist/conf/type/__package_update_index/explorer/currage b/cdist/conf/type/__package_update_index/explorer/currage index cfb778d5..8eadaf53 100644 --- a/cdist/conf/type/__package_update_index/explorer/currage +++ b/cdist/conf/type/__package_update_index/explorer/currage @@ -24,18 +24,18 @@ case "$type" in if [ -f "/var/cache/apt/pkgcache.bin" ]; then echo $(($(date +"%s")-$(stat --format '%Y' /var/cache/apt/pkgcache.bin))) else - echo 0 + echo -- -1 fi ;; pacman) if [ -d "/var/lib/pacman/sync" ]; then echo $(($(date +"%s")-$(stat --format '%Y' /var/lib/pacman/sync))) else - echo 0 + echo -- -1 fi ;; alpine) - echo 0 + echo -- -1 ;; *) echo "Your specified type ($type) is currently not supported." >&2 echo "Please contribute an implementation for it if you can." >&2 diff --git a/cdist/conf/type/__package_update_index/gencode-remote b/cdist/conf/type/__package_update_index/gencode-remote index 9b2ecba2..803468b5 100755 --- a/cdist/conf/type/__package_update_index/gencode-remote +++ b/cdist/conf/type/__package_update_index/gencode-remote @@ -31,7 +31,8 @@ if [ -n "$maxage" ]; then if [ "$type" != "apt" ] && [ "$type" != "pacman" ]; then echo "ERROR: \"--maxage\" only supported for \"apt\" or \"pacman\" pkg-manager." >&2 exit 1 - elif [ "$currage" -lt "$maxage" ]; then + # do not exit if no value found (represented as -1) + elif [ "$currage" -ne -1 ] && [ "$currage" -lt "$maxage" ]; then exit 0 # no need to update fi fi @@ -47,9 +48,9 @@ case "$type" in echo "pacman --noprogressbar --sync --refresh" echo "pacman package database synced (age was: $currage)" >> "$__messages_out" ;; - alpine) + apk) echo "apk update" - echo "apk package database updated." + echo "apk package database updated." >>"$__messages_out" ;; *) echo "Don't know how to manage packages for type: $type" >&2 diff --git a/cdist/conf/type/__pf_apply/deprecated b/cdist/conf/type/__pf_apply/deprecated new file mode 100644 index 00000000..36cfed90 --- /dev/null +++ b/cdist/conf/type/__pf_apply/deprecated @@ -0,0 +1 @@ +Consider moving to __pf_apply_anchor. Get in touch if you need __pf_apply. diff --git a/cdist/conf/type/__pf_ruleset/explorer/cksum b/cdist/conf/type/__pf_apply_anchor/gencode-remote similarity index 55% rename from cdist/conf/type/__pf_ruleset/explorer/cksum rename to cdist/conf/type/__pf_apply_anchor/gencode-remote index 9be6c901..36c26521 100755 --- a/cdist/conf/type/__pf_ruleset/explorer/cksum +++ b/cdist/conf/type/__pf_apply_anchor/gencode-remote @@ -1,6 +1,6 @@ -#!/bin/sh +#!/bin/sh -e # -# 2012 Jake Guffey (jake.guffey at eprotex.com) +# 2016 Kamila Součková (coding at kamila.is) # # This file is part of cdist. # @@ -18,24 +18,16 @@ # along with cdist. If not, see . # # -# Get the 256 bit SHA2 checksum of the pf ruleset on the target host. +# Apply pf(4) ruleset on *BSD # -# Debug -#exec >&2 -#set -x +ANCHORS_DIR="/etc/pf.d" -# Check /etc/rc.conf for pf's configuration file name. Default to /etc/pf.conf -# See if file exists and if so, get checksum - -RC="/etc/rc.conf" -TMP="$(grep '^pf_rules=' ${RC} | cut -d= -f2 | sed 's/"//g')" -PFCONF="${TMP:-"/etc/pf.conf"}" - -if [ -f "${PFCONF}" ]; then # The pf config file exists, find its cksum. - cksum -o 1 "${PFCONF}" | cut -d= -f2 | awk '{print $1}' +if [ -f "${__object}/parameter/anchor_name" ]; then + anchor_name="$(cat "${__object}/parameter/anchor_name")" +else + anchor_name="${__object_id}" fi +anchor_file="${ANCHORS_DIR}/${anchor_name}" -# Debug -#set +x - +echo "pfctl -a \"${anchor_name}\" -f \"${anchor_file}\"" diff --git a/cdist/conf/type/__pf_apply_anchor/man.rst b/cdist/conf/type/__pf_apply_anchor/man.rst new file mode 100644 index 00000000..aef6cdf4 --- /dev/null +++ b/cdist/conf/type/__pf_apply_anchor/man.rst @@ -0,0 +1,62 @@ +cdist-type__pf_apply_anchor(7) +============================== + +NAME +---- +cdist-type__pf_apply_anchor - Apply a pf(4) anchor on $__target_host + + +DESCRIPTION +----------- +This type is used on \*BSD systems to manage anchors for the pf firewall. + +Notice this type does not take care of copying the ruleset, that must be +done by the user with, e.g. `__file`. + + +OPTIONAL PARAMETERS +------------------- +anchor_name + The name of the anchor to apply. If not set, `${__object_id}` is used. + This type requires `/etc/pf.d/${anchor_name}` to exist on + `$__target_host`. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Copy anchor file to ${__target_host} + __file "/etc/pf.d/80_dns" --source - < +Kamila Součková +Jake Guffey + + +COPYING +------- +Copyright \(C) 2020 Evilham. +Copyright \(C) 2016 Kamila Součková. +Copyright \(C) 2012 Jake Guffey. 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. diff --git a/cdist/conf/type/__pf_apply_anchor/parameter/optional b/cdist/conf/type/__pf_apply_anchor/parameter/optional new file mode 100644 index 00000000..b9f61e28 --- /dev/null +++ b/cdist/conf/type/__pf_apply_anchor/parameter/optional @@ -0,0 +1 @@ +anchor_name diff --git a/cdist/conf/type/__pf_rdr/manifest b/cdist/conf/type/__pf_rdr/manifest new file mode 100644 index 00000000..39ab4470 --- /dev/null +++ b/cdist/conf/type/__pf_rdr/manifest @@ -0,0 +1,40 @@ +#!/bin/sh -e +# +# 2016 Kamila Součková (coding at kamila.is) +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +# TODO it would be cool to print a warning if a generated anchor is unused in pf.conf + +ANCHORS_DIR=/etc/pf.d + +proto="$(cat "${__object}/parameter/proto")" +from="$(cat "${__object}/parameter/from")" +to="$(cat "${__object}/parameter/to")" +state="$(cat "${__object}/parameter/state")" + +# This breaks utterly with IPv6 +from="$(echo ${from} | sed 's/:/ port /')" +to="$(echo ${to} | sed 's/:/ port /')" + +anchor_name="$(echo ${__object_id} | cut -d/ -f1)" +rule="rdr pass log proto ${proto} from any to ${from} -> ${to}" + +__directory "${ANCHORS_DIR}" --parents + +require="__directory/${ANCHORS_DIR}" \ +__line __pf_rdr/${__object_id} --state ${state} --line "${rule}" --file ${ANCHORS_DIR}/${anchor_name} diff --git a/cdist/conf/type/__pf_rdr/parameter/default/proto b/cdist/conf/type/__pf_rdr/parameter/default/proto new file mode 100644 index 00000000..28a29e6f --- /dev/null +++ b/cdist/conf/type/__pf_rdr/parameter/default/proto @@ -0,0 +1 @@ +tcp diff --git a/cdist/conf/type/__pf_rdr/parameter/default/state b/cdist/conf/type/__pf_rdr/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__pf_rdr/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__pf_rdr/parameter/optional b/cdist/conf/type/__pf_rdr/parameter/optional new file mode 100644 index 00000000..09ec92ca --- /dev/null +++ b/cdist/conf/type/__pf_rdr/parameter/optional @@ -0,0 +1,2 @@ +proto +state diff --git a/cdist/conf/type/__pf_rdr/parameter/required b/cdist/conf/type/__pf_rdr/parameter/required new file mode 100644 index 00000000..4a568482 --- /dev/null +++ b/cdist/conf/type/__pf_rdr/parameter/required @@ -0,0 +1,2 @@ +from +to diff --git a/cdist/conf/type/__pf_ruleset/gencode-local b/cdist/conf/type/__pf_ruleset/gencode-local deleted file mode 100755 index 11bfb0b1..00000000 --- a/cdist/conf/type/__pf_ruleset/gencode-local +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/sh -e -# -# 2012 Jake Guffey (jake.guffey at eprotex.com) -# -# This file is part of cdist. -# -# cdist 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. -# -# cdist 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 cdist. If not, see . -# -# -# Manage pf(4) on *BSD -# - -# Debug -#exec >&2 -#set -x - -# Send files to $__target_host via $__remote_copy - -uname=$(uname) # Need to know what the cdist host is running so we know how to compute the ruleset's checksum -state=$(cat "$__object/parameter/state") - -if [ "$state" = "absent" ]; then # There is nothing more for a *local* script to do - exit 0 -fi - -if [ -f "$__object/parameter/source" ]; then - source=$(cat "$__object/parameter/source") -fi - -rcvar=$(cat "$__object/explorer/rcvar") -cksum=$(cat "$__object/explorer/cksum") - - -cat <&2 - exit 1 - ;; -esac - -# IPv6 fix -if $(echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$') -then - my_target_host="[${__target_host}]" -else - my_target_host="${__target_host}" -fi - -if [ -n "${cksum}" ]; then - if [ ! "\${currentSum}" = "${cksum}" ]; then - $__remote_copy "${source}" "\${my_target_host}:${rcvar}.new" - fi -else # File just doesn't exist yet - $__remote_copy "${source}" "\${my_target_host}:${rcvar}.new" -fi -EOF - -# Debug -#exec +x - diff --git a/cdist/conf/type/__pf_ruleset/man.rst b/cdist/conf/type/__pf_ruleset/man.rst index 5719e94e..db8873ac 100644 --- a/cdist/conf/type/__pf_ruleset/man.rst +++ b/cdist/conf/type/__pf_ruleset/man.rst @@ -10,6 +10,9 @@ DESCRIPTION ----------- This type is used on \*BSD systems to manage the pf firewall's ruleset. +It will also enable and disable the pf firewall as requested in the `state` +parameter. + REQUIRED PARAMETERS ------------------- @@ -20,9 +23,8 @@ state OPTIONAL PARAMETERS ------------------- source - If supplied, use to define the ruleset to load onto the $__target_host for pf(4). - Note that this type is almost useless without a ruleset defined, but it's technically not - needed, e.g. for the case of disabling the firewall temporarily. + Required when state is "present". + Defines the ruleset to load onto the $__target_host for `pf(4)`. EXAMPLES @@ -30,10 +32,10 @@ EXAMPLES .. code-block:: sh - # Remove the current ruleset in place + # Remove the current ruleset in place and disable pf __pf_ruleset --state absent - # Enable the firewall with the ruleset defined in $__manifest/files/pf.conf + # Enable pf with the ruleset defined in $__manifest/files/pf.conf __pf_ruleset --state present --source $__manifest/files/pf.conf @@ -44,11 +46,13 @@ SEE ALSO AUTHORS ------- +Kamila Součková Jake Guffey COPYING ------- +Copyright \(C) 2016 Kamila Součková. Copyright \(C) 2012 Jake Guffey. 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 diff --git a/cdist/conf/type/__pf_ruleset/gencode-remote b/cdist/conf/type/__pf_ruleset/manifest similarity index 51% rename from cdist/conf/type/__pf_ruleset/gencode-remote rename to cdist/conf/type/__pf_ruleset/manifest index 12760fdf..27b35328 100755 --- a/cdist/conf/type/__pf_ruleset/gencode-remote +++ b/cdist/conf/type/__pf_ruleset/manifest @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2012 Jake Guffey (jake.guffey at eprotex.com) +# 2016 Kamila Součková (coding at kamila.is) # # This file is part of cdist. # @@ -21,29 +21,26 @@ # Manage pf(4) on *BSD # -# Debug -#exec >&2 -#set -x - -# Remove ${rcvar} in the case of --state absent - -state=$(cat "$__object/parameter/state") -rcvar=$(cat "$__object/explorer/rcvar") - -if [ "$state" = "present" ]; then # There is nothing more for a *remote* script to do - exit 0 -elif [ "$state" = "absent" ]; then - # --state absent, so ensure that .new doesn't exist and that conf is renamed to .old - cat <&2 - exit 1 +rcvar="$(cat "${__object}/explorer/rcvar")" +state="$(cat "${__object}/parameter/state")" +if [ -f "${__object}/parameter/source" ]; then + source="$(cat "${__object}/parameter/source")" fi +if [ "${state}" = "absent" ]; then + action="/etc/rc.d/pf stop" +else + action="/etc/rc.d/pf reload || /etc/rc.d/pf start" +fi + +__key_value __pf_ruleset/rcvar \ + --state "${state}" \ + --file /etc/rc.conf \ + --delimiter "=" \ + --key "pf_enable" \ + --value "YES" + +require="__key_value/__pf_ruleset/rcvar" __config_file "${rcvar}" \ + --source "${source}" \ + --state "${state}" \ + --onchange "${action}" diff --git a/cdist/conf/type/__postfix/manifest b/cdist/conf/type/__postfix/manifest index f3616979..121bba96 100755 --- a/cdist/conf/type/__postfix/manifest +++ b/cdist/conf/type/__postfix/manifest @@ -19,16 +19,4 @@ # along with cdist. If not, see . # - -os=$(cat "$__global/explorer/os") - -case "$os" in - alpine|ubuntu|debian|archlinux|suse|scientific|centos|devuan) - __package postfix --state present - ;; - *) - echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2 - echo "Please contribute an implementation for it if you can." >&2 - exit 1 - ;; -esac +__package postfix --state present diff --git a/cdist/conf/type/__postgres_database/gencode-remote b/cdist/conf/type/__postgres_database/gencode-remote index 61cfa50d..0f11cff4 100755 --- a/cdist/conf/type/__postgres_database/gencode-remote +++ b/cdist/conf/type/__postgres_database/gencode-remote @@ -41,12 +41,37 @@ if [ "$state_should" != "$state_is" ]; then present) owner="" if [ -f "$__object/parameter/owner" ]; then - owner="-O '$(cat "$__object/parameter/owner")'" + owner="-O \"$(cat "$__object/parameter/owner")\"" fi - echo "su - '$postgres_user' -c \"createdb $owner '$name'\"" + + template="" + if [ -f "$__object/parameter/template" ]; then + template="--template \"$(cat "$__object/parameter/template")\"" + fi + + encoding="" + if [ -f "$__object/parameter/encoding" ]; then + encoding="--encoding \"$(cat "$__object/parameter/encoding")\"" + fi + + lc_collate="" + if [ -f "$__object/parameter/lc-collate" ]; then + lc_collate="--lc-collate \"$(cat "$__object/parameter/lc-collate")\"" + fi + + lc_ctype="" + if [ -f "$__object/parameter/lc-ctype" ]; then + lc_ctype="--lc-ctype \"$(cat "$__object/parameter/lc-ctype")\"" + fi + + cat << EOF +su - '$postgres_user' -c "createdb $owner \"$name\" $template $encoding $lc_collate $lc_ctype" +EOF ;; absent) - echo "su - '$postgres_user' -c \"dropdb '$name'\"" + cat << EOF +su - '$postgres_user' -c "dropdb \"$name\"" +EOF ;; esac fi diff --git a/cdist/conf/type/__postgres_database/man.rst b/cdist/conf/type/__postgres_database/man.rst index acceec9b..870b4917 100644 --- a/cdist/conf/type/__postgres_database/man.rst +++ b/cdist/conf/type/__postgres_database/man.rst @@ -14,10 +14,22 @@ This cdist type allows you to create or drop postgres databases. OPTIONAL PARAMETERS ------------------- state - either 'present' or 'absent', defaults to 'present'. + Either 'present' or 'absent', defaults to 'present'. owner - the role owning this database + Specifies the database user who will own the new database. + +encoding + Specifies the character encoding scheme to be used in this database. + +lc-collate + Specifies the LC_COLLATE setting to be used in this database. + +lc-ctype + Specifies the LC_CTYPE setting to be used in this database. + +template + Specifies the template database from which to build this database. EXAMPLES diff --git a/cdist/conf/type/__postgres_database/parameter/optional b/cdist/conf/type/__postgres_database/parameter/optional index d86b6469..877fbf32 100644 --- a/cdist/conf/type/__postgres_database/parameter/optional +++ b/cdist/conf/type/__postgres_database/parameter/optional @@ -1,2 +1,6 @@ state owner +encoding +lc-collate +lc-ctype +template diff --git a/cdist/conf/type/__postgres_role/gencode-remote b/cdist/conf/type/__postgres_role/gencode-remote index fd56e85d..282294c9 100755 --- a/cdist/conf/type/__postgres_role/gencode-remote +++ b/cdist/conf/type/__postgres_role/gencode-remote @@ -53,11 +53,13 @@ case "$state_should" in done [ -n "$password" ] && password="PASSWORD '$password'" - - cmd="CREATE ROLE $name WITH $password $booleans" - echo "su - '$postgres_user' -c \"psql postgres -wc \\\"$cmd\\\"\"" + cat << EOF +su - '$postgres_user' -c "psql postgres -wc \"CREATE ROLE \\\\\"$name\\\\\" WITH $password $booleans;\"" +EOF ;; absent) - echo "su - '$postgres_user' -c \"dropuser \\\"$name\\\"\"" + cat << EOF +su - '$postgres_user' -c "dropuser \"$name\"" +EOF ;; esac diff --git a/cdist/conf/type/__pyvenv/gencode-remote b/cdist/conf/type/__pyvenv/gencode-remote index 04700683..9c7b7fab 100755 --- a/cdist/conf/type/__pyvenv/gencode-remote +++ b/cdist/conf/type/__pyvenv/gencode-remote @@ -37,11 +37,21 @@ mode="$(cat "$__object/parameter/mode")" destination="/$__object_id" venvparams="$(cat "$__object/parameter/venvparams")" pyvenvparam="$__object/parameter/pyvenv" + +os=$(cat "$__global/explorer/os") + if [ -f "$pyvenvparam" ] then pyvenv=$(cat "$pyvenvparam") else - pyvenv="pyvenv" + case "$os" in + alpine) # no pyvenv on alpine - I assume others will follow + pyvenv="python3 -m venv" + ;; + *) + pyvenv="pyvenv" + ;; + esac fi case $state_should in diff --git a/cdist/conf/type/__service/explorer/service-manager b/cdist/conf/type/__service/explorer/service-manager new file mode 100755 index 00000000..55a873fa --- /dev/null +++ b/cdist/conf/type/__service/explorer/service-manager @@ -0,0 +1,8 @@ +#!/bin/sh + +# Assume systemd if systemctl is in PATH. +if [ "$(command -v systemctl)" ]; then + printf "systemd" +else + printf "unknown" +fi diff --git a/cdist/conf/type/__service/gencode-remote b/cdist/conf/type/__service/gencode-remote new file mode 100755 index 00000000..ac62e05f --- /dev/null +++ b/cdist/conf/type/__service/gencode-remote @@ -0,0 +1,9 @@ +#!/bin/sh + +manager="$(cat "$__object/explorer/service-manager")" +name=$__object_id +action="$(cat "$__object/parameter/action")" + +if [ "$manager" = "unknown" ]; then + echo "service '$name' '$action'" +fi diff --git a/cdist/conf/type/__service/man.rst b/cdist/conf/type/__service/man.rst new file mode 100644 index 00000000..f9b23d5b --- /dev/null +++ b/cdist/conf/type/__service/man.rst @@ -0,0 +1,51 @@ +cdist-type__service(7) +====================== + +NAME +---- +cdist-type__service - Run action on a system service + + +DESCRIPTION +----------- +This type allows you to run an action against a system service. + + +REQUIRED PARAMETERS +------------------- +action + Arbitrary parameter passed as action. Usually 'start', 'stop', 'reload' or 'restart'. + +OPTIONAL PARAMETERS +------------------- +None. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Restart nginx service. + __service nginx --action restart + + # Stop postfix service. + __service postfix --action stop + + +AUTHORS +------- +Timothée Floure + + +COPYING +------- +Copyright \(C) 2019 Timothée Floure. 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. diff --git a/cdist/conf/type/__service/manifest b/cdist/conf/type/__service/manifest new file mode 100644 index 00000000..cb5af234 --- /dev/null +++ b/cdist/conf/type/__service/manifest @@ -0,0 +1,15 @@ +#!/bin/sh + +manager="$(cat "$__object/explorer/service-manager")" + +name=$__object_id +action="$(cat "$__object/parameter/action")" + +case "$manager" in + systemd) + __systemd_service "$name" --action "$action" + ;; + *) + # Unknown: handled by `service $NAME $action` in gencode-remote. + ;; +esac diff --git a/cdist/conf/type/__service/parameter/required b/cdist/conf/type/__service/parameter/required new file mode 100644 index 00000000..a9f84d41 --- /dev/null +++ b/cdist/conf/type/__service/parameter/required @@ -0,0 +1 @@ +action diff --git a/cdist/conf/type/__start_on_boot/man.rst b/cdist/conf/type/__start_on_boot/man.rst index b7c73ab1..f8afe94b 100644 --- a/cdist/conf/type/__start_on_boot/man.rst +++ b/cdist/conf/type/__start_on_boot/man.rst @@ -12,7 +12,7 @@ This cdist type allows you to enable or disable stuff to be started at boot of your operating system. Warning: This type has not been tested intensively and is not fully -supported (i.e. \*BSD are not implemented). +supported. REQUIRED PARAMETERS diff --git a/cdist/conf/type/__systemd_service/explorer/state b/cdist/conf/type/__systemd_service/explorer/state new file mode 100755 index 00000000..f5f751d4 --- /dev/null +++ b/cdist/conf/type/__systemd_service/explorer/state @@ -0,0 +1,43 @@ +#!/bin/sh -e +# explorer/state +# +# 2020 Matthias Stecher +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +# Check if the service is running or stopped. +# +# The explorer must check before if the service exist, because 'systemctl is-active' +# will return "inactive" even if there is no service there: +# systemctl cat foo # does not exist +# systemctl is-active foo # is "inactive" + + +# get name of the service +if [ -f "$__object/parameter/name" ]; then + name="$(cat "$__object/parameter/name")" +else + name="$__object_id" +fi + + +# check if the service exist, else exit without output (also if systemd doesn't exist) +# do not exit here with an error code, will be done in the gencode-remote script +systemctl cat "$name" > /dev/null 2>&1 || exit 0 + +# print if the service is running or not +systemctl is-active -q "$name" && printf "running" || printf "stopped" diff --git a/cdist/conf/type/__systemd_service/gencode-remote b/cdist/conf/type/__systemd_service/gencode-remote new file mode 100755 index 00000000..c867ff22 --- /dev/null +++ b/cdist/conf/type/__systemd_service/gencode-remote @@ -0,0 +1,98 @@ +#!/bin/sh -e +# gencode-remote +# +# 2020 Matthias Stecher +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# + +# Checks the given state of the service and set it to the given +# state. Optionally, it executes the action if service running. + + +# get name of the service +name="$__object/parameter/name" +if [ -f "$name" ]; then + name="$(cat "$name")" +else + name="$__object_id" +fi + + +# read current status and parameters +state="$(cat "$__object/explorer/state")" +should="$(cat "$__object/parameter/state")" + +# if systemd/service does not exist +if [ -z "$state" ]; then + printf "systemd or service '%s' does not exist!\n" "$name" >&2 + exit 1 +fi + + +# save the action required +required_action="" + +# check the state of the service that should be +if [ "$state" != "$should" ]; then + # select what to do to get the $should state + case "$should" in + running) + if [ "$state" = "stopped" ]; then required_action="start"; fi + ;; + + stopped) + if [ "$state" = "running" ]; then required_action="stop"; fi + ;; + esac +fi + +# check if the action can be achieved if given +if [ -f "$__object/parameter/action" ] \ + && [ -z "$required_action" ] && [ "$state" = "running" ]; then + + # there must be an action + action="$(cat "$__object/parameter/action")" + + # select the action to the required element + case "$action" in + restart) + required_action="restart" + ;; + + reload) + required_action="reload" + ;; + + *) + printf "action '%s' does not exist!" "$action" >&2 + exit 2 + esac + + # Make a special check: only do this action if a dependency did something + # it is required that the dependencies write there action to $__messages_in + if [ -f "$__object/parameter/if-required" ]; then + # exit here if there are no changes from the dependencies affected (nothing to do) + if ! grep -q -f "$__object/require" "$__messages_in"; then exit 0; fi + fi +fi + +# print the execution command if a action given +if [ -n "$required_action" ]; then + # also print it as message + echo "$required_action" >> "$__messages_out" + echo "systemctl $required_action '$name'" +fi diff --git a/cdist/conf/type/__systemd_service/man.rst b/cdist/conf/type/__systemd_service/man.rst new file mode 100644 index 00000000..7eca398b --- /dev/null +++ b/cdist/conf/type/__systemd_service/man.rst @@ -0,0 +1,110 @@ +cdist-type__systemd-service(7) +============================== + +NAME +---- +cdist-type__systemd-service - Controls a systemd service state + +DESCRIPTION +----------- +This type controls systemd services to define a state of the service, +or an action like reloading or restarting. It is useful to reload a +service after configuration applied or shutdown one service. + +The activation or deactivation is out of scope. Look for the +:strong:`cdist-type__systemd_util`\ (7) type instead. + +REQUIRED PARAMETERS +------------------- + +None. + +OPTIONAL PARAMETERS +------------------- + +name + String which will used as name instead of the object id. + +state + The state which the service should be in: + + running + Service should run (default) + + stoppend + Service should stopped + +action + Executes an action on on the service. It will only execute it if the + service keeps the state **running**. There are following actions, where: + + reload + Reloads the service + + restart + Restarts the service + +BOOLEAN PARAMETERS +------------------ + +if-required + Only execute the action if minimum one required type outputs a message to + **$__messages_out**. Through this, the action should only executed if a + dependency did something. The action will not executed if no dependencies + given. + +MESSAGES +-------- + +start + Started the service + +stop + Stopped the service + +restart + Restarted the service + +reload + Reloaded the service + +ABORTS +------ +Aborts in following cases: + +systemd or the service does not exist + +EXAMPLES +-------- +.. code-block:: sh + + # service must run + __systemd_service nginx + + # service must stopped + __systemd_service sshd \ + --state stopped + + # restart the service + __systemd_service apache2 \ + --action restart + + # makes sure the service exist with an alternative name + __systemd_service foo \ + --name sshd + + # reload the service for a modified configuration file + # only reloads the service if the file really changed + require="__config_file/etc/foo.conf" __systemd_service foo \ + --action reload --if-required + +AUTHORS +------- +Matthias Stecher + +COPYRIGHT +--------- +Copyright \(C) 2020 Matthias Stecher. 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. diff --git a/cdist/conf/type/__systemd_service/parameter/boolean b/cdist/conf/type/__systemd_service/parameter/boolean new file mode 100644 index 00000000..a4bccb66 --- /dev/null +++ b/cdist/conf/type/__systemd_service/parameter/boolean @@ -0,0 +1 @@ +if-required diff --git a/cdist/conf/type/__systemd_service/parameter/default/state b/cdist/conf/type/__systemd_service/parameter/default/state new file mode 100644 index 00000000..a2ae71b3 --- /dev/null +++ b/cdist/conf/type/__systemd_service/parameter/default/state @@ -0,0 +1 @@ +running diff --git a/cdist/conf/type/__systemd_service/parameter/optional b/cdist/conf/type/__systemd_service/parameter/optional new file mode 100644 index 00000000..fc78265f --- /dev/null +++ b/cdist/conf/type/__systemd_service/parameter/optional @@ -0,0 +1,3 @@ +name +state +action diff --git a/cdist/conf/type/__tinydns/gencode-remote b/cdist/conf/type/__tinydns/gencode-remote new file mode 100644 index 00000000..824479b6 --- /dev/null +++ b/cdist/conf/type/__tinydns/gencode-remote @@ -0,0 +1,7 @@ +servicename=$__object_id +user="$(cat "$__object/parameter/user")" +server_ip="$(cat "$__object/parameter/server-ip")" + +cat</dev/null || ./add-host $name $ip +make +EOF diff --git a/cdist/conf/type/__tinydns_host/manifest b/cdist/conf/type/__tinydns_host/manifest new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__tinydns_host/parameter/required b/cdist/conf/type/__tinydns_host/parameter/required new file mode 100644 index 00000000..93d111b2 --- /dev/null +++ b/cdist/conf/type/__tinydns_host/parameter/required @@ -0,0 +1 @@ +ip diff --git a/cdist/conf/type/__tinydns_ns/gencode-remote b/cdist/conf/type/__tinydns_ns/gencode-remote new file mode 100644 index 00000000..7305e605 --- /dev/null +++ b/cdist/conf/type/__tinydns_ns/gencode-remote @@ -0,0 +1,13 @@ +set -x + +servicename=$(echo $__object_id | cut -d/ -f1) +name=$(echo $__object_id | cut -d/ -f2-) +ip="$(cat "$__object/parameter/ip")" + +cat</dev/null || ./add-ns $name $ip +make +EOF + +set +x diff --git a/cdist/conf/type/__tinydns_ns/parameter/required b/cdist/conf/type/__tinydns_ns/parameter/required new file mode 100644 index 00000000..93d111b2 --- /dev/null +++ b/cdist/conf/type/__tinydns_ns/parameter/required @@ -0,0 +1 @@ +ip diff --git a/cdist/conf/type/__update_alternatives/explorer/state b/cdist/conf/type/__update_alternatives/explorer/state new file mode 100755 index 00000000..04a78aaa --- /dev/null +++ b/cdist/conf/type/__update_alternatives/explorer/state @@ -0,0 +1,8 @@ +#!/bin/sh -e +path="$(cat "$__object/parameter/path")" +name="$__object_id" +link="$(readlink "/etc/alternatives/$name")" +if [ "$path" = "$link" ] +then echo present +else echo absent +fi diff --git a/cdist/conf/type/__update_alternatives/gencode-remote b/cdist/conf/type/__update_alternatives/gencode-remote index 0e7b0d89..c0b49814 100755 --- a/cdist/conf/type/__update_alternatives/gencode-remote +++ b/cdist/conf/type/__update_alternatives/gencode-remote @@ -17,9 +17,10 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -# Setup alternative - no standard way to create, always set -# + +if [ "$(cat "$__object/explorer/state")" = 'present' ] +then exit 0 +fi path="$(cat "$__object/parameter/path")" name="$__object_id" diff --git a/cdist/conf/type/__user/explorer/shadow b/cdist/conf/type/__user/explorer/shadow index 73ce0e29..63d38f0d 100755 --- a/cdist/conf/type/__user/explorer/shadow +++ b/cdist/conf/type/__user/explorer/shadow @@ -24,7 +24,7 @@ name=$__object_id case $("$__explorer/os") in - 'freebsd'|'netbsd'|'openbsd') + 'freebsd'|'netbsd'|'openbsd'|'alpine') database='passwd' ;; # Default to using shadow passwords diff --git a/cdist/configuration.py b/cdist/configuration.py index f05a5963..1011a382 100644 --- a/cdist/configuration.py +++ b/cdist/configuration.py @@ -274,7 +274,8 @@ class Configuration(metaclass=Singleton): os.path.isfile(_local_config_file))): _local_config_file = os.path.join( os.environ.get('XDG_CONFIG_HOME', - os.path.expanduser('~/.config/cdist')), + os.path.expanduser('~/.config/')), + 'cdist', _config_basename) _dist_config_file = os.path.join( os.path.abspath(os.path.join(os.path.dirname(cdist.__file__), "conf")), diff --git a/cdist/exec/local.py b/cdist/exec/local.py index f83c85df..ad6c6e36 100644 --- a/cdist/exec/local.py +++ b/cdist/exec/local.py @@ -69,7 +69,6 @@ class Local(object): self.exec_path = exec_path self.custom_initial_manifest = initial_manifest - self._add_conf_dirs = add_conf_dirs self.cache_path_pattern = cache_path_pattern self.quiet_mode = quiet_mode if configuration: @@ -84,16 +83,7 @@ class Local(object): self._init_cache_dir(None) self._init_paths() self._init_object_marker() - self._init_conf_dirs() - - @property - def dist_conf_dir(self): - return os.path.abspath(os.path.join(os.path.dirname(cdist.__file__), - "conf")) - - @property - def home_dir(self): - return cdist.home_dir() + self._init_conf_dirs(add_conf_dirs) def _init_log(self): self.log = logging.getLogger(self.target_host[0]) @@ -140,28 +130,9 @@ class Local(object): # Does not need to be secure - just randomly different from .cdist self.object_marker_name = tempfile.mktemp(prefix='.cdist-', dir='') - def _init_conf_dirs(self): - self.conf_dirs = [] - - self.conf_dirs.append(self.dist_conf_dir) - - # Is the default place for user created explorer, type and manifest - if self.home_dir: - self.conf_dirs.append(self.home_dir) - - # Add directories defined in the CDIST_PATH environment variable - # if 'CDIST_PATH' in os.environ: - # cdist_path_dirs = re.split(r'(?. +# +# + +import cdist +import cdist.configuration +import cdist.core +import cdist.exec.util as util +import os +import glob +import fnmatch + + +class Info(object): + + def __init__(self, conf_dirs, args): + self.conf_dirs = conf_dirs + self.all = args.all + self.display_global_explorers = args.global_explorers + self.display_types = args.types + if not self.display_global_explorers and not self.display_types: + self.all = True + self.fixed_string = args.fixed_string + self._setup_glob_pattern(args.pattern) + self.full = args.full + + def _setup_glob_pattern(self, pattern): + if pattern is None: + self.glob_pattern = '*' + elif ('?' in pattern or '*' in pattern or '[' in pattern or + self.fixed_string): + self.glob_pattern = pattern + else: + self.glob_pattern = '*' + pattern + '*' + + @classmethod + def commandline(cls, args): + conf_dirs = util.resolve_conf_dirs_from_config_and_args(args) + c = cls(conf_dirs, args) + c.run() + + def _get_global_explorers(self, conf_path): + rv = [] + global_explorer_path = os.path.join(conf_path, "explorer", + self.glob_pattern) + if self.fixed_string: + if os.path.exists(global_explorer_path): + rv.append(global_explorer_path) + else: + for explorer in glob.glob(global_explorer_path): + rv.append(explorer) + return rv + + def _should_display_type(self, dir_entry): + if not dir_entry.is_dir(): + return False + if self.glob_pattern is None: + return True + if self.fixed_string: + return dir_entry.name == self.glob_pattern + else: + return fnmatch.fnmatch(dir_entry.name, self.glob_pattern) + + def _get_types(self, conf_path): + rv = [] + types_path = os.path.join(conf_path, "type") + if not os.path.exists(types_path): + return rv + with os.scandir(types_path) as it: + for entry in it: + if self._should_display_type(entry): + rv.append(entry.path) + return rv + + def _display_details(self, title, details, default_values=None, + deprecated=None): + if not details: + return + if isinstance(details, bool): + print("\t{}: {}".format(title, 'yes' if details else 'no')) + elif isinstance(details, str): + print("\t{}: {}".format(title, details)) + elif isinstance(details, list): + dv = dict(default_values) if default_values else {} + dp = dict(deprecated) if deprecated else {} + + print("\t{}:".format(title)) + for x in sorted(details): + print("\t\t{}".format(x), end='') + has_default = x in dv + is_deprecated = x in dp + need_comma = False + if has_default or is_deprecated: + print(" (", end='') + if has_default: + print("default: {}".format(dv[x]), end='') + need_comma = True + if is_deprecated: + print("{}deprecated".format(', ' if need_comma else ''), + end='') + if has_default or is_deprecated: + print(")", end='') + print() + + def _display_type_parameters(self, cdist_type): + self._display_details("required parameters", + cdist_type.required_parameters, + default_values=cdist_type.parameter_defaults, + deprecated=cdist_type.deprecated_parameters) + self._display_details("required multiple parameters", + cdist_type.required_multiple_parameters, + default_values=cdist_type.parameter_defaults, + deprecated=cdist_type.deprecated_parameters) + self._display_details("optional parameters", + cdist_type.optional_parameters, + default_values=cdist_type.parameter_defaults, + deprecated=cdist_type.deprecated_parameters) + self._display_details("optional multiple parameters", + cdist_type.optional_multiple_parameters, + default_values=cdist_type.parameter_defaults, + deprecated=cdist_type.deprecated_parameters) + self._display_details("boolean parameters", + cdist_type.boolean_parameters, + default_values=cdist_type.parameter_defaults, + deprecated=cdist_type.deprecated_parameters) + + def _display_type_characteristics(self, cdist_type): + characteristics = [] + if cdist_type.is_install: + characteristics.append('install') + else: + characteristics.append('config') + if cdist_type.is_singleton: + characteristics.append('singleton') + if cdist_type.is_nonparallel: + characteristics.append('nonparallel') + else: + characteristics.append('parallel') + if cdist_type.deprecated is not None: + characteristics.append('deprecated') + print("\t{}".format(', '.join(characteristics))) + + def _display_type_details(self, type_path): + dirname, basename = os.path.split(type_path) + cdist_type = cdist.core.CdistType(dirname, basename) + + self._display_type_characteristics(cdist_type) + self._display_type_parameters(cdist_type) + + def run(self): + rv = [] + for cp in self.conf_dirs: + conf_path = os.path.expanduser(cp) + if self.all or self.display_global_explorers: + rv.extend((x, 'E', ) for x in self._get_global_explorers( + conf_path)) + if self.all or self.display_types: + rv.extend((x, 'T', ) for x in self._get_types(conf_path)) + rv = sorted(rv, key=lambda x: x[0]) + for x, t in rv: + print(x) + if self.full and t == 'T': + self._display_type_details(x) diff --git a/cdist/preos.py b/cdist/preos.py index 46b45554..bf2a8e60 100644 --- a/cdist/preos.py +++ b/cdist/preos.py @@ -5,6 +5,9 @@ import inspect import argparse import cdist import logging +import cdist.argparse +import cdist.configuration +import cdist.exec.util as util _PREOS_CALL = "commandline" @@ -12,17 +15,16 @@ _PREOS_NAME = "_preos_name" _PREOS_MARKER = "_cdist_preos" _PLUGINS_DIR = "preos" _PLUGINS_PATH = [os.path.join(os.path.dirname(__file__), _PLUGINS_DIR), ] -cdist_home = cdist.home_dir() -if cdist_home: - cdist_home_preos = os.path.join(cdist_home, "preos") - if os.path.isdir(cdist_home_preos): - _PLUGINS_PATH.append(cdist_home_preos) -sys.path.extend(_PLUGINS_PATH) - - log = logging.getLogger("PreOS") +def extend_plugins_path(dirs): + for dir in dirs: + preos_dir = os.path.expanduser(os.path.join(dir, "preos")) + if os.path.isdir(preos_dir): + _PLUGINS_PATH.append(preos_dir) + + def preos_plugin(obj): """It is preos if _PREOS_MARKER is True and has _PREOS_CALL.""" if hasattr(obj, _PREOS_MARKER): @@ -71,31 +73,58 @@ def check_root(): raise cdist.Error("Must be run with root privileges") +def get_available_preoses_string(cls): + preoses = [' - {}'.format(x) for x in sorted(set(cls.preoses))] + return "Available PreOS-es:\n{}".format("\n".join(preoses)) + + class PreOS(object): preoses = None @classmethod def commandline(cls, argv): - - if not cls.preoses: - cls.preoses = find_preoses() - + cdist_parser = cdist.argparse.get_parsers() parser = argparse.ArgumentParser( - description="Create PreOS", prog="cdist preos") - parser.add_argument('preos', help='PreOS to create, one of: {}'.format( - set(cls.preoses))) - args = parser.parse_args(argv[1:2]) + description="Create PreOS", prog="cdist preos", + parents=[cdist_parser['loglevel'], ]) + parser.add_argument('preos', help='PreOS to create', + nargs='?', default=None) + parser.add_argument('-c', '--conf-dir', + help=('Add configuration directory (one that ' + 'contains "preos" subdirectory)'), + action='append') + parser.add_argument('-g', '--config-file', + help='Use specified custom configuration file.', + dest="config_file", required=False) + parser.add_argument('-L', '--list-preoses', + help='List available PreOS-es', + action='store_true', default=False) + parser.add_argument('remainder_args', nargs=argparse.REMAINDER) + args = parser.parse_args(argv[1:]) + cdist.argparse.handle_loglevel(args) + log.debug("preos args : {}".format(args)) + + conf_dirs = util.resolve_conf_dirs_from_config_and_args(args) + + extend_plugins_path(conf_dirs) + sys.path.extend(_PLUGINS_PATH) + cls.preoses = find_preoses() + + if args.list_preoses or not args.preos: + print(get_available_preoses_string(cls)) + sys.exit(0) preos_name = args.preos if preos_name in cls.preoses: preos = cls.preoses[preos_name] func = getattr(preos, _PREOS_CALL) if inspect.ismodule(preos): - func_args = [preos, argv[2:], ] + func_args = [preos, args.remainder_args, ] else: - func_args = [argv[2:], ] + func_args = [args.remainder_args, ] log.info("Running preos : {}".format(preos_name)) func(*func_args) else: - log.error("Unknown preos: {}, available preoses: {}".format( - preos_name, set(cls.preoses.keys()))) + raise cdist.Error( + "Invalid PreOS {}. {}".format( + preos_name, get_available_preoses_string(cls))) diff --git a/docs/changelog b/docs/changelog index 09e30e79..7cacf3cf 100644 --- a/docs/changelog +++ b/docs/changelog @@ -1,6 +1,81 @@ Changelog --------- +next: + * Core: Fix XDG_CONFIG_HOME config file location (Joachim Desroches) + * Type __postgres_database: Add encoding, lc-collate, lc-ctype, template parameters (Timothée Floure) + * Type __motd: Improve documentation and support for FreeBSD (Evil Ham) + * Type __openldap_server: Support extra config parameter (Evil Ham) + * Type __postfix: Automagically support more OSs by not checking too much (Evil Ham) + * New type: __pf_apply_anchor (Kamila Součková, Evil Ham) + * Type __pf_ruleset: Refactor (Kamila Součková, Evil Ham) + * Type __pf_apply: Deprecate type (Kamila Součková, Evil Ham) + +6.5.4: 2020-04-11 + * Explorer init: Do not grep on non-existent init (Steven Armstrong) + * Core: Bugfix to preserve conf dirs order (Steven Armstrong) + +6.5.3: 2020-04-03 + * Type __cron: Make non parallel due to race condition (Nico Schottelius) + * Type __pyvenv: Use python3 -m venv on Alpine (Nico Schottelius) + * Type __user: Fix missing shadow for alpine (llnu) + * Type __consule_agent: Make conf_dir dependent on OS - fixes Alpine (Nico Schottelius) + * Type __letsencrypt_cert: Fix typo (Andrew Schleifer) + * Type __package_update_index: Fix maxage false positives (Matthias Stecher) + +6.5.2: 2020-02-27 + * Type __update_alternatives: Add state explorer (Ander Punnar) + * Explorer os_version: Add support for Alpine Linux (Jin-Guk Kwon) + * Explorer init: Rewrite and support more init systems (Dennis Camera) + * New type: __service (Timothée Floure) + * Types __consul_*: Add optional parameter for using distribution packages (Timothée Floure) + * Explorer disks: Fix NetBSD, support Linux w/o lsblk (Dennis Camera) + * Type __directory: Add 'exists' and 'pre-exists' states (Dennis Camera) + * Type __file: Improve error messages for pre-exists state (Dennis Camera) + +6.5.1: 2020-02-15 + * Type __consul_agent: Add Debian 10 support (Nico Schottelius) + * Explorer os_release: Add fallbacks (Dennis Camera) + * Types __file, __directory: Add fallback for systems without stat (Dennis Camera) + * Type __mysql_privileges: Fix quoting (Ander Punnar) + * Type __package_apt: Update package index if it is older than one day (Ander Punnar) + * Type __cron: Fix job removal if 'is' and 'should' don't match (Matthias Stecher) + * New type: __systemd_service (Matthias Stecher) + * Type __postgres_role: Fix password command syntax (Timothée Floure) + +6.5.0: 2020-01-23 + * Type __acl: Add --entry parameter to replace --acl, deprecate --acl (Ander Punnar) + * Core: preos: Fix missing configuration file usage, support -g, --config-file option (Darko Poljak) + * Core info command: Support tilde expansion of conf directories (Darko Poljak) + * Types __postgres_*: Fix edge cases in quoted identifiers (Timothée Floure) + * New type: __apt_unattended_upgrades (Ander Punnar) + * Type __line: Bugfixes: (Dennis Camera) + - ensure the line is only added once + - always add line to end if anchor is not found + - match line at the beginning when not regex + - fix incorrect 'wrongposition' in state explorer + - produce error when file does not exist + * Type __acl: Add --source, --file and --directory parameters (Ander Punnar) + +6.4.0: 2020-01-04 + * Type __consul_agent: Don't deploy init script on Alpine anymore, it ships with one itself (Nico Schottelius) + * Type __install_chroot_umount: Bugfix: type was not using __chroot_umount/manifest (Steven Armstrong) + * Types __postgres_*: Use double quoted identifiers in generated SQL (Timothée Floure) + * Core: Add cdist info command (Darko Poljak) + * New types: __mysql_user, __mysql_privileges (Ander Punnar) + * Type __mysql_database: Rewrite (Ander Punnar) + +6.3.0: 2019-12-12 + * Type __package_update_index: Fix Alpine part (Dominique Roux) + * Documentation: Fix man pages for install types (Darko Poljak) + * Documentation: Embed config skeleton instead of rewriting it (Darko Poljak) + * Documentation: Remove cdist-type prefix and man page reference from type list in html (Darko Poljak) + * Documentation: PreOS english nitpicking (Evil Ham) + * Documentation: Add installing from source with signature verification (Darko Poljak) + * Core: preos: Support top command logging options, custom conf-dir option and CDIST_PATH env var (Darko Poljak) + * Type __start_on_boot: Docs: remove unsupported *BSD claim (Evil Ham) + * New type: __openldap_server (Evil Ham) + 6.2.0: 2019-11-30 * Core: Redefine/reimplement/fix CDIST_ORDER_DEPENDENCY (Darko Poljak) diff --git a/docs/src/cdist-configuration.rst b/docs/src/cdist-configuration.rst index 4c9b4d33..706ed761 100644 --- a/docs/src/cdist-configuration.rst +++ b/docs/src/cdist-configuration.rst @@ -31,73 +31,11 @@ can be used. Config file format ------------------ + cdist configuration file is in the INI file format. Currently it supports only [GLOBAL] section. -The possible keywords and their meanings are as follows: -:strong:`archiving` - Use specified archiving. Valid values include: - 'none', 'tar', 'tgz', 'tbz2' and 'txz'. +Here you can find configuration file skeleton: -:strong:`beta` - Enable beta functionality. It recognizes boolean values from - 'yes'/'no', 'on'/'off', 'true'/'false' and '1'/'0'. - -:strong:`cache_path_pattern` - Specify cache path pattern. - -:strong:`conf_dir` - List of configuration directories separated with the character conventionally - used by the operating system to separate search path components (as in PATH), - such as ':' for POSIX or ';' for Windows. - If also specified at command line then values from command line are - appended to this value. - -:strong:`init_manifest` - Specify default initial manifest. - -:strong:`inventory_dir` - Specify inventory directory. - -:strong:`jobs` - Specify number of jobs for parallel processing. If -1 then the default, - number of CPU's in the system is used. If 0 then parallel processing in - jobs is disabled. If set to positive number then specified maximum - number of processes will be used. - -:strong:`local_shell` - Shell command used for local execution. - -:strong:`out_path` - Directory to save cdist output in. - -:strong:`parallel` - Process hosts in parallel. If -1 then the default, number of CPU's in - the system is used. If 0 then parallel processing of hosts is disabled. - If set to positive number then specified maximum number of processes - will be used. - -:strong:`remote_copy` - Command to use for remote copy (should behave like scp). - -:strong:`remote_exec` - Command to use for remote execution (should behave like ssh). - -:strong:`remote_out_path` - Directory to save cdist output in on the target host. - -:strong:`remote_shell` - Shell command at remote host used for remote execution. - -:strong:`save_output_streams` - Enable/disable saving output streams (enabled by default). - It recognizes boolean values from 'yes'/'no', 'on'/'off', 'true'/'false' - and '1'/'0'. - -:strong:`timestamp` - Timestamp log messages with the current local date and time - in the format: YYYYMMDDHHMMSS.us. - -:strong:`verbosity` - Set verbosity level. Valid values are: - 'ERROR', 'WARNING', 'INFO', 'VERBOSE', 'DEBUG', 'TRACE' and 'OFF'. +.. literalinclude:: cdist.cfg.skeleton + :language: ini diff --git a/docs/src/cdist-install.rst b/docs/src/cdist-install.rst index a9b7d6b5..880b9f8e 100644 --- a/docs/src/cdist-install.rst +++ b/docs/src/cdist-install.rst @@ -23,8 +23,6 @@ Target Hosts Install cdist ------------- -You can install cdist either from git or as a python package. - From git ~~~~~~~~ @@ -42,6 +40,7 @@ To install cdist, execute the following commands: From version 4.2.0 cdist tags and releases are signed. You can get GPG public key used for signing `here <_static/pgp-key-EFD2AE4EC36B6901.asc>`_. +It is assumed that you are familiar with *git* ways of signing and verification. You can also get cdist from `github mirror `_. @@ -157,3 +156,27 @@ Cdist is available as a python package at .. code-block:: sh pip install cdist + +Installing from source with signature verification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want to install cdist from signed source and verify it, first you need to +download cdist archive and its detached signature. + +Get both, *cdist-x.y.z.tar.gz* and *cdist-x.y.z.tar.gz.asc* from release +notes of the desired tag *x.y.z* at +`cdist git repository `_. + +Get GPG public key used for signing `here <_static/pgp-key-EFD2AE4EC36B6901.asc>`_ +and import it into GPG. + +Now cdist source archive can be verified using `gpg`, e.g. to verify `cdist-6.2.0`: + +.. code-block:: sh + + $ gpg --verify cdist-6.2.0.tar.gz.asc cdist-6.2.0.targ.gz + gpg: Signature made Sat Nov 30 23:14:19 2019 CET + gpg: using RSA key 69767822F3ECC3C349C1EFFFEFD2AE4EC36B6901 + gpg: Good signature from "ungleich GmbH (ungleich FOSS) " [ultimate] + +Further steps are the same as for `installing from git `_. diff --git a/docs/src/cdist-preos.rst b/docs/src/cdist-preos.rst index e85af2de..9570bcfc 100644 --- a/docs/src/cdist-preos.rst +++ b/docs/src/cdist-preos.rst @@ -4,9 +4,9 @@ PreOS Description ----------- With cdist you can install and configure new machines. You can use cdist to -create PreOS, minimal OS which purpose is to boot new machine. -After PreOS is booted machine is ready for installing desired OS and -then it is ready for configuration. +create PreOS, minimal OS whose purpose is to boot a new machine. +After PreOS is booted, the machine is ready for installing the desired OS and +afterwards it is ready for configuration. PreOS creation -------------- @@ -15,62 +15,62 @@ Currently supported PreOS-es include: * debian * ubuntu -* devuan. +* devuan -PreOS is created using cdist preos command. preos command has subcommands that -create the desired PreOS. +PreOS is created using the ``cdist preos`` command. +This command has subcommands that determine the desired PreOS. -For example, to create ubuntu PreOS: +For example, to create an ubuntu PreOS: .. code-block:: sh $ cdist preos ubuntu /preos/preos-ubuntu -B -C \ -k ~/.ssh/id_rsa.pub -p /preos/pxe-ubuntu -For more info about available options see cdist manual page. +For more info about the available options see the cdist manual page. -This will bootstrap (``-B``) ubuntu PreOS in ``/preos/preos-ubuntu`` directory, it -will be configured (``-C``) using default built-in initial manifest and with -specified ssh authorized key (``-k``). -After bootstrapping and configuration PXE -boot directory will be created (``-p``) in ``/preos/pxe-ubuntu``. +This will bootstrap (``-B``) ubuntu PreOS in the ``/preos/preos-ubuntu`` +directory, it will be configured (``-C``) using default the built-in initial +manifest and with specified ssh authorized key (``-k``). +After bootstrapping and configuration, the PXE boot directory will be +created (``-p``) in ``/preos/pxe-ubuntu``. -After PreOS is created new machines can be booted using created PXE (after -proper dhcp, tftp setting). +After PreOS is created, new machines can be booted using the created PXE +(after proper dhcp and tftp settings). -Since PreOS is configured with ssh authorized key it can be accessed throguh +Since PreOS is configured with ssh authorized key it can be accessed through ssh, i.e. it can be further installed and configured with cdist. -Implementing new PreOS sub-command ----------------------------------- +Implementing a new PreOS sub-command +------------------------------------ preos command is implemented as a plugin system. This plugin system scans for -preos subcommands in ``cdist/preos/`` distribution directory and also in +preos subcommands in the ``cdist/preos/`` distribution directory and also in ``~/.cdist/preos/`` directory if it exists. preos subcommand is a module or a class that satisfies the following: -* it has attribute ``_cdist_preos`` set to ``True`` -* it has function/method ``commandline``. +* it has the attribute ``_cdist_preos`` set to ``True`` +* it defines a function/method ``commandline``. -For a module based preos subcommand ``commandline`` function accepts a module -object as its first argument and the list of command line +For a module-based preos subcommand, the ``commandline`` function accepts a +module object as its first argument and the list of command line arguments (``sys.argv[2:]``). -For a class preos subcommand ``commandline`` method should be staticmethod and -it accepts a class object as its first argument and the list of command line -arguments(``sys.argv[2:]``). +For a class-based preos subcommand ``commandline`` method should be +static-method and must accept a class as its first argument and the +list of command line arguments (``sys.argv[2:]``). If preos scanning finds a module/class that has ``_cdist_preos`` set -to ``True`` and it has function/method ``commandline`` then this module/class is +to ``True`` and a function/method ``commandline`` then this module/class is registered to preos subcommands. The name of the command is set to ``_preos_name`` -attribute if it exists, otherwise it is set to the module/class name, lowercase. -When registered preos subcommand is specified as preos command then ``commandline`` -will be called with first argument set to module/class object and second argument -set to ``sys.argv[2:]``. +attribute if defined in the module/class, defaulting to the module/class name in lowercase. +When a registered preos subcommand is specified, ``commandline`` +will be called with the first argument set to module/class and the second +argument set to ``sys.argv[2:]``. -Example writing new dummy preos sub-command -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Module based preos: +Example of writing new dummy preos sub-command +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Module-based preos: ^^^^^^^^^^^^^^^^^^^ #. Create directory ``~/.cdist/preos/`` if it does not exist @@ -88,16 +88,12 @@ When you try to run this new preos you will get: .. code-block:: sh - $ cdist preos -h - usage: cdist preos [-h] preos - - Create PreOS - - positional arguments: - preos PreOS to create, one of: {'netbsd', 'debian', 'ubuntu'} - - optional arguments: - -h, --help show this help message and exit + $ cdist preos -L + Available PreOS-es: + - debian + - devuan + - netbsd + - ubuntu $ cdist preos netbsd NetBSD PreOS: [] @@ -121,17 +117,13 @@ When you try to run this new preos you will get: .. code-block:: sh $ cdist preos -h - usage: cdist preos [-h] preos - - Create PreOS - - positional arguments: - preos PreOS to create, one of: {'freebsd', 'debian', 'ubuntu'} - - optional arguments: - -h, --help show this help message and exit + Available PreOS-es: + - debian + - devuan + - freebsd + - ubuntu $ cdist preos freebsd FreeBSD dummy preos: [] In the ``commandline`` function/method you have all the freedom to actually create -PreOS. +a PreOS. diff --git a/docs/src/cdist-types.rst b/docs/src/cdist-types.rst deleted file mode 100644 index d5104667..00000000 --- a/docs/src/cdist-types.rst +++ /dev/null @@ -1,8 +0,0 @@ -cdist types -=========== - -.. toctree:: - :titlesonly: - :glob: - - man7/* diff --git a/docs/src/cdist-types.rst.sh b/docs/src/cdist-types.rst.sh new file mode 100755 index 00000000..2eb66009 --- /dev/null +++ b/docs/src/cdist-types.rst.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# +# 2019 Darko Poljak (darko.poljak at gmail.com) +# +# This file is part of cdist. +# +# cdist 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. +# +# cdist 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 cdist. If not, see . +# +# +# Generate cdist-types.rst that lists available types. +# + +__cdist_pwd="$(pwd -P)" +__cdist_mydir="${0%/*}"; +__cdist_abs_mydir="$(cd "$__cdist_mydir" && pwd -P)" +__cdist_myname=${0##*/}; +__cdist_abs_myname="$__cdist_abs_mydir/$__cdist_myname" + +filename="${__cdist_myname%.sh}" +dest="$__cdist_abs_mydir/$filename" + +cd "$__cdist_abs_mydir" + +exec > "$dest" +cat << eof +cdist types +=========== + +.. toctree:: + :titlesonly: + +eof + +# If there is no such file then ls prints error to stderr, +# so redirect stderr to /dev/null. +for type in $(ls man7/cdist-type__*.rst 2>/dev/null | LC_ALL=C sort); do + no_dir="${type#man7/}"; + no_type="${no_dir#cdist-type}"; + name="${no_type%.rst}"; + manref="${no_dir%.rst}" + man="${manref}(7)" + + echo " $name" "" +done diff --git a/docs/src/conf.py b/docs/src/conf.py index 78f9842c..47765413 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -56,7 +56,7 @@ master_doc = 'index' # General information about the project. project = 'cdist' -copyright = 'ungleich GmbH 2019' +copyright = 'ungleich GmbH 2020' # author = 'Darko Poljak' # The version info for the project you're documenting, acts as replacement for diff --git a/docs/src/index.rst b/docs/src/index.rst index 5e54d8fc..31c044dc 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -2,8 +2,9 @@ cdist - usable configuration management ======================================= cdist is a usable configuration management system. -It adheres to the KISS principle and +It adheres to the KISS principle and is being used in small up to enterprise grade environments. +It natively supports IPv6 since the first release. .. toctree:: diff --git a/docs/src/man1/cdist.rst b/docs/src/man1/cdist.rst index f40491e9..38248821 100644 --- a/docs/src/man1/cdist.rst +++ b/docs/src/man1/cdist.rst @@ -11,7 +11,7 @@ SYNOPSIS :: - cdist [-h] [-V] {banner,config,install,inventory,preos,shell} ... + cdist [-h] [-V] {banner,config,install,inventory,preos,shell,info} ... cdist banner [-h] [-l LOGLEVEL] [-q] [-v] @@ -59,31 +59,35 @@ SYNOPSIS [-I INVENTORY_DIR] [-a] [-f HOSTFILE] [-H] [-t] [host [host ...]] - cdist preos [-h] preos + cdist preos [-h] [-l LOGLEVEL] [-q] [-v] [-c CONF_DIR] [-g CONFIG_FILE] + [-L] + [preos] ... - cdist preos debian [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-a ARCH] [-B] - [-C] [-c CDIST_PARAMS] [-D DRIVE] [-e REMOTE_EXEC] - [-i MANIFEST] [-k KEYFILE ] [-m MIRROR] - [-P ROOT_PASSWORD] [-p PXE_BOOT_DIR] [-r] - [-S SCRIPT] [-s SUITE] [-y REMOTE_COPY] - target_dir + cdist preos [preos-options] debian [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-a ARCH] [-B] + [-C] [-c CDIST_PARAMS] [-D DRIVE] [-e REMOTE_EXEC] + [-i MANIFEST] [-k KEYFILE ] [-m MIRROR] + [-P ROOT_PASSWORD] [-p PXE_BOOT_DIR] [-r] + [-S SCRIPT] [-s SUITE] [-y REMOTE_COPY] + target_dir - cdist preos devuan [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-a ARCH] [-B] - [-C] [-c CDIST_PARAMS] [-D DRIVE] [-e REMOTE_EXEC] - [-i MANIFEST] [-k KEYFILE ] [-m MIRROR] - [-P ROOT_PASSWORD] [-p PXE_BOOT_DIR] [-r] - [-S SCRIPT] [-s SUITE] [-y REMOTE_COPY] - target_dir + cdist preos [preos-options] devuan [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-a ARCH] [-B] + [-C] [-c CDIST_PARAMS] [-D DRIVE] [-e REMOTE_EXEC] + [-i MANIFEST] [-k KEYFILE ] [-m MIRROR] + [-P ROOT_PASSWORD] [-p PXE_BOOT_DIR] [-r] + [-S SCRIPT] [-s SUITE] [-y REMOTE_COPY] + target_dir - cdist preos ubuntu [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-a ARCH] [-B] - [-C] [-c CDIST_PARAMS] [-D DRIVE] [-e REMOTE_EXEC] - [-i MANIFEST] [-k KEYFILE ] [-m MIRROR] - [-P ROOT_PASSWORD] [-p PXE_BOOT_DIR] [-r] - [-S SCRIPT] [-s SUITE] [-y REMOTE_COPY] - target_dir + cdist preos [preos-options] ubuntu [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-a ARCH] [-B] + [-C] [-c CDIST_PARAMS] [-D DRIVE] [-e REMOTE_EXEC] + [-i MANIFEST] [-k KEYFILE ] [-m MIRROR] + [-P ROOT_PASSWORD] [-p PXE_BOOT_DIR] [-r] + [-S SCRIPT] [-s SUITE] [-y REMOTE_COPY] + target_dir cdist shell [-h] [-l LOGLEVEL] [-q] [-v] [-s SHELL] + cdist info [-h] [-a] [-c CONF_DIR] [-e] [-F] [-f] [-g CONFIG_FILE] [-t] [pattern] + DESCRIPTION ----------- @@ -103,7 +107,7 @@ All commands accept the following options: **-l LOGLEVEL, --log-level LOGLEVEL** Set the specified verbosity level. The levels, in order from the lowest to the highest, are: ERROR (-1), - WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) TRACE (4 + WARNING (0), INFO (1), VERBOSE (2), DEBUG (3), TRACE (4 or higher). If used along with -v then -v increases last set value and -l overwrites last set value. @@ -116,7 +120,7 @@ All commands accept the following options: value is 0 which includes ERROR and WARNING levels. The levels, in order from the lowest to the highest, are: ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), - DEBUG (3) TRACE (4 or higher). If used along with -l + DEBUG (3), TRACE (4 or higher). If used along with -l then -l overwrites last set value and -v increases last set value. @@ -457,7 +461,18 @@ List inventory database. PREOS ----- -Create PreOS. Currently, the following PreOS-es are supported: +Create PreOS. + +**-c CONF_DIR, --conf-dir CONF_DIR** + Add configuration directory (one that contains "preos" subdirectory). + +**-g CONFIG_FILE, --config-file CONFIG_FILE** + Use specified custom configuration file. + +**-L, --list-preoses** + List available PreOS-es. + +Currently, the following PreOS-es are supported: * debian * ubuntu @@ -596,6 +611,39 @@ usage. Its primary use is for debugging type parameters. be POSIX compatible shell. +INFO +---- +Display information for cdist (global explorers, types). + +**pattern** + Glob pattern. If it contains special characters('?', '*', '[') then it is + used as specified, otherwise it is translated to `*pattern*`. + +**-h, --help** + Show help message and exit. + +**-a, --all** + Display all info. This is the default. + +**-c CONF_DIR, --conf-dir CONF_DIR** + Add configuration directory (can be repeated). + +**-e, --global-explorers** + Display info for global explorers. + +**-F, --fixed-string** + Interpret pattern as a fixed string. + +**-f, --full** + Display full details. + +**-g CONFIG_FILE, --config-file CONFIG_FILE** + Use specified custom configuration file. + +**-t, --types** + Display info for types. + + CONFIGURATION ------------- cdist obtains configuration data from the following sources in the following @@ -907,5 +955,5 @@ such case and display a warning message. An example of such a case: COPYING ------- -Copyright \(C) 2011-2017 Nico Schottelius. Free use of this software is +Copyright \(C) 2011-2019 Nico Schottelius. Free use of this software is granted under the terms of the GNU General Public License v3 or later (GPLv3+). diff --git a/other/ci/Dockerfile b/other/ci/Dockerfile new file mode 100644 index 00000000..03d6b546 --- /dev/null +++ b/other/ci/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine:latest + +COPY ./repositories /etc/apk/ + +RUN apk update +RUN apk upgrade +RUN apk add python3 py3-pycodestyle rsync make shellcheck git +RUN apk fix diff --git a/other/ci/README.md b/other/ci/README.md new file mode 100644 index 00000000..6a9b0ac5 --- /dev/null +++ b/other/ci/README.md @@ -0,0 +1,3 @@ +This container is used for cdist's CI pipeline, and deployed in ungleich's docker registry at: + + code.ungleich.ch:5050/ungleich-public/cdist/cdist-ci:latest diff --git a/other/ci/repositories b/other/ci/repositories new file mode 100644 index 00000000..46cabcc3 --- /dev/null +++ b/other/ci/repositories @@ -0,0 +1,3 @@ +https://mirror.ungleich.ch/mirror/packages/alpine/edge/main +https://mirror.ungleich.ch/mirror/packages/alpine/edge/community +https://mirror.ungleich.ch/mirror/packages/alpine/edge/testing diff --git a/scripts/cdist-dump b/scripts/cdist-dump index 83b09eb8..d29e5985 100755 --- a/scripts/cdist-dump +++ b/scripts/cdist-dump @@ -224,6 +224,7 @@ hor_line() if [ "${do_global_explorer}" ] then print_verbose 2 "Dumping global explorers" + # shellcheck disable=SC2086 set -- "$@" ${or} \( \ -path "*/explorer/*" -a \ ! -path "*/conf/*" -a \