commit 711ed7363448af7f22f00eb7a17c38aec497d362 Author: Christian Brauner Date: Wed Feb 21 14:51:18 2018 +0100 initial commit Signed-off-by: Christian Brauner diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..7e36433 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +IBM Corporation. diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 0000000..4e8aa38 --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1,107 @@ +Contributing to this project +---------------------------- + +This project accepts contributions. In order to contribute, you should +pay attention to a few things: + + 1 - your code must follow the coding style rules + 2 - the format of the submission must Github pull requests + 3 - your work must be signed + + +Coding Style: +------------- + +The coding style follows the Linux kernel coding style. + +The coding style is defined in the 'CodingStyle' file, available in +the directory 'Documentation' of the Linux kernel source tree. + +It can be accessed online too: + +https://www.kernel.org/doc/html/v4.10/process/coding-style.html + +Submitting Modifications: +------------------------- + +The contributions must be Github pull requests. + +Licensing for new files: +------------------------ + +LXC is made of files shipped under a few different licenses. + +Anything that ends up being part of the LXC library needs to be released +under LGPLv2.1+ or a license compatible with it (though the latter will +only be accepted for cases where the code originated elsewhere and was +imported into LXC). + +Language bindings for the libraries need to be released under LGPLv2.1+. + +Anything else (non-libaries) needs to be Free Software and needs to be +allowed to link with LGPLv2.1+ code (if needed). LXC upstream prefers +LGPLv2.1+ or GPLv2 for those. + +When introducing a new file into the project, please make sure it has a +copyright header making clear under which license it's being released +and if it doesn't match the criteria described above, please explain +your decision on the lxc-devel mailing-list when submitting your patch. + +Developer Certificate of Origin: +-------------------------------- + +To improve tracking of contributions to this project we will use a +process modeled on the modified DCO 1.1 and use a "sign-off" procedure. + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right +to pass it on as an open-source patch. The rules are pretty simple: +if you can certify the below: + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I have + the right to submit it under the open source license indicated in + the file; or + +(b) The contribution is based upon previous work that, to the best of + my knowledge, is covered under an appropriate open source License + and I have the right under that license to submit that work with + modifications, whether created in whole or in part by me, under + the same open source license (unless I am permitted to submit + under a different license), as indicated in the file; or + +(c) The contribution was provided directly to me by some other person + who certified (a), (b) or (c) and I have not modified it. + +(d) The contribution is made free of any other party's intellectual + property claims or rights. + +(e) I understand and agree that this project and the contribution are + public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. + + +then you just add a line saying + + Signed-off-by: Random J Developer + +You can do it by using option -s or --signoff when you commit + + git commit --signoff ... + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +In addition we support the following DCOs which maintainers can use to indicate +that a patch is acceptable: + + Acked-by: Random J Developer + Reviewed-by: Random J Developer + +If you are contributing as a group who is implementing a feature together such +that it cannot be reasonably attributed to a single developer please use: + + Co-developed-by: Random J Developer 1 + Co-developed-by: Random J Developer 2 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..aad6071 --- /dev/null +++ b/INSTALL @@ -0,0 +1,239 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./autogen.sh; ./configure; make; make install' +should configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 0. If the sources are not coming from a package maintainer and the + 'configure' file does not exist, you should run './autogen.sh' in + the directory containing the package's source code in order to + generate the 'configure' file from the 'configure.ac' file. + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..6e126c1 --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,12 @@ +Before submitting your patches, check they are signed-off-by +conforming with the DCO contained in the ./CONTRIBUTING file. + +Maintainer +---------- + +Committers : Serge Hallyn, Stéphane Graber, Dwight Engen and Christian Brauner +Mail patches to : lxc-devel@lists.linuxcontainers.org +Send pull requests at : https://github.com/lxc/lxc +Mailing lists : lxc-devel@lists.linuxcontainers.org, lxc-users@lists.linuxcontainers.org +Web page : https://linuxcontainers.org/lxc +GIT location : git://github.com/lxc/lxc diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..cd18371 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,18 @@ +# Makefile.am + +ACLOCAL_AMFLAGS = -I config + +LIBTOOL_DEPS = @LIBTOOL_DEPS@ +SUBDIRS = config templates +DIST_SUBDIRS = config templates +EXTRA_DIST = \ + autogen.sh \ + CONTRIBUTING \ + MAINTAINERS + +install-data-local: + $(MKDIR_P) $(DESTDIR)$(LXCPATH) + $(MKDIR_P) $(DESTDIR)$(localstatedir)/cache/lxc + +ChangeLog:: + @touch ChangeLog diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..5c98e87 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# lxc: linux Container library +# +# (C) Copyright IBM Corp. 2007, 2008 +# +# Authors: +# Daniel Lezcano +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +set -x + +test -d autom4te.cache && rm -rf autom4te.cache +aclocal -I config || exit 1 +autoconf || exit 1 +automake --add-missing --copy || exit 1 diff --git a/config/Makefile.am b/config/Makefile.am new file mode 100644 index 0000000..904d18a --- /dev/null +++ b/config/Makefile.am @@ -0,0 +1,37 @@ +templatesconfigdir=@LXCTEMPLATECONFIG@ + +templatesconfig_DATA = \ + alpine.common.conf \ + alpine.userns.conf \ + archlinux.common.conf \ + archlinux.userns.conf \ + centos.common.conf \ + centos.userns.conf \ + debian.common.conf \ + debian.userns.conf \ + fedora.common.conf \ + fedora.userns.conf \ + gentoo.common.conf \ + gentoo.moresecure.conf \ + gentoo.userns.conf \ + opensuse.common.conf \ + opensuse.userns.conf \ + oracle.common.conf \ + oracle.userns.conf \ + plamo.common.conf \ + plamo.userns.conf \ + slackware.common.conf \ + slackware.userns.conf \ + ubuntu-cloud.common.conf \ + ubuntu-cloud.lucid.conf \ + ubuntu-cloud.userns.conf \ + ubuntu.common.conf \ + ubuntu.lucid.conf \ + ubuntu.userns.conf \ + openwrt.common.conf \ + sparclinux.common.conf \ + sparclinux.userns.conf \ + voidlinux.common.conf \ + voidlinux.userns.conf \ + sabayon.common.conf \ + sabayon.userns.conf diff --git a/config/acinclude.m4 b/config/acinclude.m4 new file mode 100644 index 0000000..d718b5e --- /dev/null +++ b/config/acinclude.m4 @@ -0,0 +1,137 @@ +dnl as-ac-expand.m4 0.2.0 +dnl autostars m4 macro for expanding directories using configure's prefix +dnl thomas@apestaart.org +dnl + +dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) +dnl example +dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) +dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local + +AC_DEFUN([AS_AC_EXPAND], +[ + EXP_VAR=[$1] + FROM_VAR=[$2] + + dnl first expand prefix and exec_prefix if necessary + prefix_save=$prefix + exec_prefix_save=$exec_prefix + + dnl if no prefix given, then use /usr/local, the default prefix + if test "x$prefix" = "xNONE"; then + prefix="$ac_default_prefix" + fi + dnl if no exec_prefix given, then use prefix + if test "x$exec_prefix" = "xNONE"; then + exec_prefix=$prefix + fi + + full_var="$FROM_VAR" + dnl loop until it doesn't change anymore + while true; do + new_full_var="`eval echo $full_var`" + if test "x$new_full_var" = "x$full_var"; then break; fi + full_var=$new_full_var + done + + dnl clean up + full_var=$new_full_var + AC_SUBST([$1], "$full_var") + + dnl restore prefix and exec_prefix + prefix=$prefix_save + exec_prefix=$exec_prefix_save +]) + +dnl Available from the GNU Autoconf Macro Archive at: +dnl http://www.gnu.org/software/ac-archive/htmldoc/ax_compare_version.html +AC_DEFUN([AX_COMPARE_VERSION], [ +# Used to indicate true or false condition +ax_compare_version=false + # Convert the two version strings to be compared into a format that + # allows a simple string comparison. The end result is that a version + # string of the form 1.12.5-r617 will be converted to the form + # 0001001200050617. In other words, each number is zero padded to four + # digits, and non digits are removed. + AS_VAR_PUSHDEF([A],[ax_compare_version_A]) + A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + AS_VAR_PUSHDEF([B],[ax_compare_version_B]) + B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary + dnl # then the first line is used to determine if the condition is true. + dnl # The sed right after the echo is to remove any indented white space. + m4_case(m4_tolower($2), + [lt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [gt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [le],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ], + [ge],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ],[ + dnl Split the operator from the subversion count if present. + m4_bmatch(m4_substr($2,2), + [0],[ + # A count of zero means use the length of the shorter version. + # Determine the number of characters in A and B. + ax_compare_version_len_A=`echo "$A" | awk '{print(length)}'` + ax_compare_version_len_B=`echo "$B" | awk '{print(length)}'` + + # Set A to no more than B's length and B to no more than A's length. + A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` + B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` + ], + [[0-9]+],[ + # A count greater than zero means use only that many subversions + A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + ], + [.+],[ + AC_WARNING( + [illegal OP numeric parameter: $2]) + ],[]) + + # Pad zeros at end of numbers to make same length. + ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" + B="$B`echo $A | sed 's/./0/g'`" + A="$ax_compare_version_tmp_A" + + # Check for equality or inequality as necessary. + m4_case(m4_tolower(m4_substr($2,0,2)), + [eq],[ + test "x$A" = "x$B" && ax_compare_version=true + ], + [ne],[ + test "x$A" != "x$B" && ax_compare_version=true + ],[ + AC_WARNING([illegal OP parameter: $2]) + ]) + ]) + + AS_VAR_POPDEF([A])dnl + AS_VAR_POPDEF([B])dnl + + dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. + if test "$ax_compare_version" = "true" ; then + m4_ifvaln([$4],[$4],[:])dnl + m4_ifvaln([$5],[else $5])dnl + fi +]) dnl AX_COMPARE_VERSION diff --git a/config/alpine.common.conf.in b/config/alpine.common.conf.in new file mode 100644 index 0000000..1c4cf81 --- /dev/null +++ b/config/alpine.common.conf.in @@ -0,0 +1,24 @@ +# This derives from the global common config. +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Doesn't support consoles in /dev/lxc/. +lxc.tty.dir = + +# Drop another (potentially) harmful capabilities. +lxc.cap.drop = audit_write +lxc.cap.drop = ipc_owner +lxc.cap.drop = mknod +lxc.cap.drop = setpcap +lxc.cap.drop = sys_nice +lxc.cap.drop = sys_pacct +lxc.cap.drop = sys_rawio +lxc.cap.drop = sys_resource +lxc.cap.drop = sys_tty_config +lxc.cap.drop = syslog +lxc.cap.drop = wake_alarm + +# Mount /run as tmpfs. +lxc.mount.entry=run run tmpfs rw,nodev,relatime,mode=755 0 0 + +# Mount /dev/shm as tmpfs; needed for building python and possibly other packages. +lxc.mount.entry=shm dev/shm tmpfs rw,nodev,noexec,nosuid,relatime,mode=1777,create=dir 0 0 diff --git a/config/alpine.userns.conf.in b/config/alpine.userns.conf.in new file mode 100644 index 0000000..4336b44 --- /dev/null +++ b/config/alpine.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config. +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/archlinux.common.conf.in b/config/archlinux.common.conf.in new file mode 100644 index 0000000..81d6548 --- /dev/null +++ b/config/archlinux.common.conf.in @@ -0,0 +1,31 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Allow for 6 tty devices by default +lxc.tty.max = 6 + +# Set the halt/stop signals +lxc.signal.halt=SIGRTMIN+4 + +# Uncomment to disable creating tty devices subdirectory in /dev +# lxc.tty.dir = + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# lxc.cap.drop = sys_admin +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +# lxc.cap.drop = setpcap # big big login delays in Fedora 20 systemd +# +lxc.cap.drop = setfcap sys_nice sys_pacct sys_rawio diff --git a/config/archlinux.userns.conf.in b/config/archlinux.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/archlinux.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/centos.common.conf.in b/config/centos.common.conf.in new file mode 100644 index 0000000..8a72ad0 --- /dev/null +++ b/config/centos.common.conf.in @@ -0,0 +1,20 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# lxc.cap.drop = sys_admin +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +lxc.cap.drop = sys_nice sys_pacct sys_rawio diff --git a/config/centos.userns.conf.in b/config/centos.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/centos.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/debian.common.conf.in b/config/debian.common.conf.in new file mode 100644 index 0000000..4e6a6e6 --- /dev/null +++ b/config/debian.common.conf.in @@ -0,0 +1,28 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Doesn't support consoles in /dev/lxc/ +lxc.tty.dir = + +# When using LXC with apparmor, the container will be confined by default. +# If you wish for it to instead run unconfined, copy the following line +# (uncommented) to the container's configuration file. +#lxc.apparmor.profile = unconfined + +# If you wish to allow mounting block filesystems, then use the following +# line instead, and make sure to grant access to the block device and/or loop +# devices below in lxc.cgroup.devices.allow. +#lxc.apparmor.profile = lxc-container-default-with-mounting + +# Extra cgroup device access +## rtc +lxc.cgroup.devices.allow = c 254:0 rm +## tun +lxc.cgroup.devices.allow = c 10:200 rwm +## hpet +lxc.cgroup.devices.allow = c 10:228 rwm +## kvm +lxc.cgroup.devices.allow = c 10:232 rwm +## To use loop devices, copy the following line to the container's +## configuration file (uncommented). +#lxc.cgroup.devices.allow = b 7:* rwm diff --git a/config/debian.userns.conf.in b/config/debian.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/debian.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/fedora.common.conf.in b/config/fedora.common.conf.in new file mode 100644 index 0000000..acebe3c --- /dev/null +++ b/config/fedora.common.conf.in @@ -0,0 +1,21 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# lxc.cap.drop = sys_admin +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +# lxc.cap.drop = setpcap # big big login delays in Fedora 20 systemd +lxc.cap.drop = setfcap sys_nice sys_pacct sys_rawio diff --git a/config/fedora.userns.conf.in b/config/fedora.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/fedora.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/gentoo.common.conf.in b/config/gentoo.common.conf.in new file mode 100644 index 0000000..477a2ab --- /dev/null +++ b/config/gentoo.common.conf.in @@ -0,0 +1,27 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Gentoo common default configuration +# This is the most feature-full container configuration +# But security is not the goal. +# Looking for more security, see gentoo.moresecure.conf + +# Doesn't support consoles in /dev/lxc/ +lxc.tty.dir = + +# Extra cgroup device access +## rtc +lxc.cgroup.devices.allow = c 254:0 rm +## tun +lxc.cgroup.devices.allow = c 10:200 rwm +## hpet +lxc.cgroup.devices.allow = c 10:228 rwm +## kvm +lxc.cgroup.devices.allow = c 10:232 rwm +## To use loop devices, copy the following line to the container's +## configuration file (uncommented). +#lxc.cgroup.devices.allow = b 7:* rwm + +# /dev/shm needs to be mounted as tmpfs. It's needed by python (bug #496328) +# and possibly other packages. +lxc.mount.entry = none dev/shm tmpfs rw,nosuid,nodev,create=dir diff --git a/config/gentoo.moresecure.conf.in b/config/gentoo.moresecure.conf.in new file mode 100644 index 0000000..aa7c625 --- /dev/null +++ b/config/gentoo.moresecure.conf.in @@ -0,0 +1,45 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Gentoo security oriented default configuration +# This is a more security oriented container configuration +# "More" because this is far from fully secure +# Looking for more working features and you trust your +# Container user ? see gentoo.common.conf + +# do not mount sysfs, see http://blog.bofh.it/debian/id_413 +lxc.mount.entry=mqueue dev/mqueue mqueue rw,nodev,noexec,nosuid,create=dir 0 0 +lxc.mount.entry=shm dev/shm tmpfs rw,nosuid,nodev,noexec,relatime,create=dir 0 0 +lxc.mount.entry=run run tmpfs rw,nosuid,nodev,relatime,mode=755 0 0 + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# lxc.cap.drop = sys_admin # breaks systemd +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +# lxc.cap.drop = setpcap # breaks journald +# lxc.cap.drop = sys_resource # breaks systemd +# lxc.cap.drop = sys_boot # breaks sysvinit +lxc.cap.drop = audit_control audit_write dac_read_search fsetid ipc_owner linux_immutable mknod setfcap setpcap sys_admin sys_nice sys_pacct sys_ptrace sys_rawio sys_resource sys_tty_config syslog + +# WARNING: the security vulnerability reported for 'cap_net_admin' at +# http://mainisusuallyafunction.blogspot.com/2012/11/attacking-hardened-linux-systems-with.html +# via JIT spraying (the BPF JIT module disabled on most systems was used +# in the example, but others are suggested vulnerable) meant that users +# with root in a container, that capability and kernel module may escape +# the container. ALWAYS be extremely careful granting any process root +# within a container, use a minimal configuration at all levels - +# including the kernel - and multiple layers of security on any system +# where security is a priority. note that not only LXC but PAX (and +# others?) were vulnerable to this issue. diff --git a/config/gentoo.userns.conf.in b/config/gentoo.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/gentoo.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/opensuse.common.conf.in b/config/opensuse.common.conf.in new file mode 100644 index 0000000..b040e95 --- /dev/null +++ b/config/opensuse.common.conf.in @@ -0,0 +1,22 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# lxc.cap.drop = sys_admin +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +# lxc.cap.drop = setpcap # big big login delays in Fedora 20 systemd +# lxc.cap.drop = setfcap +lxc.cap.drop = sys_nice sys_pacct sys_rawio diff --git a/config/opensuse.userns.conf.in b/config/opensuse.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/opensuse.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/openwrt.common.conf.in b/config/openwrt.common.conf.in new file mode 100644 index 0000000..73db6f6 --- /dev/null +++ b/config/openwrt.common.conf.in @@ -0,0 +1,50 @@ +# Default console settings +lxc.tty.dir = lxc +lxc.tty.max = 4 +lxc.pty.max = 1024 + +# Default capabilities +lxc.cap.drop = mac_admin +lxc.cap.drop = mac_override +lxc.cap.drop = sys_admin +lxc.cap.drop = sys_module +lxc.cap.drop = sys_nice +lxc.cap.drop = sys_pacct +lxc.cap.drop = sys_ptrace +lxc.cap.drop = sys_rawio +lxc.cap.drop = sys_resource +lxc.cap.drop = sys_time +lxc.cap.drop = sys_tty_config +lxc.cap.drop = syslog +lxc.cap.drop = wake_alarm + +# Default cgroups - all denied except those whitelisted +lxc.cgroup.devices.deny = a +## /dev/null and zero +lxc.cgroup.devices.allow = c 1:3 rwm +lxc.cgroup.devices.allow = c 1:5 rwm +## consoles +lxc.cgroup.devices.allow = c 5:0 rwm +lxc.cgroup.devices.allow = c 5:1 rwm +## /dev/{,u}random +lxc.cgroup.devices.allow = c 1:8 rwm +lxc.cgroup.devices.allow = c 1:9 rwm +## /dev/pts/* +lxc.cgroup.devices.allow = c 5:2 rwm +lxc.cgroup.devices.allow = c 136:* rwm +## rtc +lxc.cgroup.devices.allow = c 254:0 rm +## tun +lxc.cgroup.devices.allow = c 10:200 rwm +## dev/tty0 +lxc.cgroup.devices.allow = c 4:0 rwm +## dev/tty1 +lxc.cgroup.devices.allow = c 4:1 rwm + +## To use loop devices, copy the following line to the container's +## configuration file (uncommented). +#lxc.cgroup.devices.allow = b 7:* rwm + +# Blacklist some syscalls which are not safe in privileged +# containers +lxc.seccomp.profile = /usr/share/lxc/config/common.seccomp diff --git a/config/oracle.common.conf.in b/config/oracle.common.conf.in new file mode 100644 index 0000000..8a72ad0 --- /dev/null +++ b/config/oracle.common.conf.in @@ -0,0 +1,20 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# lxc.cap.drop = sys_admin +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +lxc.cap.drop = sys_nice sys_pacct sys_rawio diff --git a/config/oracle.userns.conf.in b/config/oracle.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/oracle.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/plamo.common.conf.in b/config/plamo.common.conf.in new file mode 100644 index 0000000..7918b4a --- /dev/null +++ b/config/plamo.common.conf.in @@ -0,0 +1,9 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Doesn't support consoles in /dev/lxc/ +lxc.tty.dir = + +# Extra cgroup device access +## rtc +lxc.cgroup.devices.allow = c 254:0 rm diff --git a/config/plamo.userns.conf.in b/config/plamo.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/plamo.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/sabayon.common.conf.in b/config/sabayon.common.conf.in new file mode 100644 index 0000000..ccb4c12 --- /dev/null +++ b/config/sabayon.common.conf.in @@ -0,0 +1,79 @@ +# Default configuration for Sabayon containers + +# Setup the default mounts +lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed + +# Allow for 1024 pseudo terminals +lxc.pty.max = 1024 + +# Setup 1 tty devices for lxc-console command +lxc.tty.max = 1 + +# Needed for systemd distro +lxc.autodev = 1 + +# Doesn't support consoles in /dev/lxc/ +lxc.tty.dir = + +# CGroup whitelist +lxc.cgroup.devices.deny = a + +## Allow any mknod (but not reading/writing the node) +#lxc.cgroup.devices.allow = c *:* m +#lxc.cgroup.devices.allow = b *:* m + +## Allow specific devices +### /dev/null +lxc.cgroup.devices.allow = c 1:3 rwm +### /dev/zero +lxc.cgroup.devices.allow = c 1:5 rwm +### /dev/full +lxc.cgroup.devices.allow = c 1:7 rwm +### /dev/random +lxc.cgroup.devices.allow = c 1:8 rwm +### /dev/urandom +lxc.cgroup.devices.allow = c 1:9 rwm +### /dev/pts/* +#lxc.cgroup.devices.allow = c 136:* rwm +### /dev/tty +#lxc.cgroup.devices.allow = c 5:0 rwm +### /dev/console +#lxc.cgroup.devices.allow = c 5:1 rwm +### /dev/ptmx +#lxc.cgroup.devices.allow = c 5:2 rwm +### fuse +#lxc.cgroup.devices.allow = c 10:229 rwm +## To use loop devices, copy the following line to the container's +## configuration file (uncommented). +#lxc.cgroup.devices.allow = b 7:* rwm +## rtc +#lxc.cgroup.devices.allow = c 254:0 rm +## tun +#lxc.cgroup.devices.allow = c 10:200 rwm +## hpet +#lxc.cgroup.devices.allow = c 10:228 rwm +## kvm +#lxc.cgroup.devices.allow = c 10:232 rwm +## /dev/mem +#lxc.cgroup.devices.allow = c 1:1 rwm + +# If something doesn't work, try to comment this out. +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +lxc.cap.drop = sys_time sys_module sys_rawio mac_admin mac_override +#lxc.cap.drop = sys_admin + + +# /dev/shm needs to be mounted as tmpfs. It's needed by python (bug #496328) +# and possibly other packages. +lxc.mount.entry = none dev/shm tmpfs rw,nosuid,nodev,create=dir + +# Blacklist some syscalls which are not safe in privileged +# containers +lxc.seccomp.profile = @LXCTEMPLATECONFIG@/common.seccomp + +# Customize lxc options through common directory +lxc.include = @LXCTEMPLATECONFIG@/common.conf.d/ diff --git a/config/sabayon.userns.conf.in b/config/sabayon.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/sabayon.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/slackware.common.conf.in b/config/slackware.common.conf.in new file mode 100644 index 0000000..899c52c --- /dev/null +++ b/config/slackware.common.conf.in @@ -0,0 +1,46 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Doesn't support consoles in /dev/lxc/ +lxc.tty.dir = + +# Extra cgroup device access +## rtc +lxc.cgroup.devices.allow = c 254:0 rm +## tun +lxc.cgroup.devices.allow = c 10:200 rwm +## hpet +lxc.cgroup.devices.allow = c 10:228 rwm +## kvm +lxc.cgroup.devices.allow = c 10:232 rwm +## To use loop devices, copy the following line to the container's +## configuration file (uncommented). +#lxc.cgroup.devices.allow = b 7:* rwm + +# mount /dev/shm as tmpfs +lxc.mount.entry = none dev/shm tmpfs rw,nosuid,nodev,create=dir + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# Note that some are already dropped in common.conf. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# +# Some of these don't apply in Slackware but are here for future reference. +# +# lxc.cap.drop = sys_admin # breaks systemd +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +# lxc.cap.drop = setpcap # breaks journald +# lxc.cap.drop = sys_resource # breaks systemd +# +lxc.cap.drop = mknod setfcap setpcap diff --git a/config/slackware.userns.conf.in b/config/slackware.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/slackware.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/sparclinux.common.conf.in b/config/sparclinux.common.conf.in new file mode 100644 index 0000000..8a72ad0 --- /dev/null +++ b/config/sparclinux.common.conf.in @@ -0,0 +1,20 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# lxc.cap.drop = sys_admin +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +lxc.cap.drop = sys_nice sys_pacct sys_rawio diff --git a/config/sparclinux.userns.conf.in b/config/sparclinux.userns.conf.in new file mode 100644 index 0000000..707bb30 --- /dev/null +++ b/config/sparclinux.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf diff --git a/config/ubuntu-cloud.common.conf.in b/config/ubuntu-cloud.common.conf.in new file mode 100644 index 0000000..4d67722 --- /dev/null +++ b/config/ubuntu-cloud.common.conf.in @@ -0,0 +1,4 @@ +# This derives from the main Ubuntu config +lxc.include = @LXCTEMPLATECONFIG@/ubuntu.common.conf + +lxc.hook.clone = @LXCHOOKDIR@/ubuntu-cloud-prep diff --git a/config/ubuntu-cloud.lucid.conf.in b/config/ubuntu-cloud.lucid.conf.in new file mode 100644 index 0000000..78205e5 --- /dev/null +++ b/config/ubuntu-cloud.lucid.conf.in @@ -0,0 +1,2 @@ +# This derives from the main Ubuntu lucid config +lxc.include = @LXCTEMPLATECONFIG@/ubuntu.lucid.conf diff --git a/config/ubuntu-cloud.userns.conf.in b/config/ubuntu-cloud.userns.conf.in new file mode 100644 index 0000000..e1baca8 --- /dev/null +++ b/config/ubuntu-cloud.userns.conf.in @@ -0,0 +1,2 @@ +# This derives from the main Ubuntu userns config +lxc.include = @LXCTEMPLATECONFIG@/ubuntu.userns.conf diff --git a/config/ubuntu.common.conf.in b/config/ubuntu.common.conf.in new file mode 100644 index 0000000..ff8ef6a --- /dev/null +++ b/config/ubuntu.common.conf.in @@ -0,0 +1,35 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Default mount entries +lxc.mount.entry = /sys/kernel/debug sys/kernel/debug none bind,optional 0 0 +lxc.mount.entry = /sys/kernel/security sys/kernel/security none bind,optional 0 0 +lxc.mount.entry = /sys/fs/pstore sys/fs/pstore none bind,optional 0 0 +lxc.mount.entry = mqueue dev/mqueue mqueue rw,relatime,create=dir,optional 0 0 + +# When using LXC with apparmor, the container will be confined by default. +# If you wish for it to instead run unconfined, copy the following line +# (uncommented) to the container's configuration file. +#lxc.apparmor.profile = unconfined + +# Uncomment the following line to autodetect squid-deb-proxy configuration on the +# host and forward it to the guest at start time. +#lxc.hook.pre-start = /usr/share/lxc/hooks/squid-deb-proxy-client + +# If you wish to allow mounting block filesystems, then use the following +# line instead, and make sure to grant access to the block device and/or loop +# devices below in lxc.cgroup.devices.allow. +#lxc.apparmor.profile = lxc-container-default-with-mounting + +# Extra cgroup device access +## rtc +lxc.cgroup.devices.allow = c 254:0 rm +## tun +lxc.cgroup.devices.allow = c 10:200 rwm +## hpet +lxc.cgroup.devices.allow = c 10:228 rwm +## kvm +lxc.cgroup.devices.allow = c 10:232 rwm +## To use loop devices, copy the following line to the container's +## configuration file (uncommented). +#lxc.cgroup.devices.allow = b 7:* rwm diff --git a/config/ubuntu.lucid.conf.in b/config/ubuntu.lucid.conf.in new file mode 100644 index 0000000..b6fe37c --- /dev/null +++ b/config/ubuntu.lucid.conf.in @@ -0,0 +1,2 @@ +# Ubuntu 10.04 LTS doesn't have /dev/lxc/ +lxc.tty.dir = diff --git a/config/ubuntu.userns.conf.in b/config/ubuntu.userns.conf.in new file mode 100644 index 0000000..0d73464 --- /dev/null +++ b/config/ubuntu.userns.conf.in @@ -0,0 +1,6 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf + +# Extra fstab entries as mountall can't mount those by itself +lxc.mount.entry = /sys/firmware/efi/efivars sys/firmware/efi/efivars none bind,optional 0 0 +lxc.mount.entry = /proc/sys/fs/binfmt_misc proc/sys/fs/binfmt_misc none bind,optional 0 0 diff --git a/config/voidlinux.common.conf.in b/config/voidlinux.common.conf.in new file mode 100644 index 0000000..c287b45 --- /dev/null +++ b/config/voidlinux.common.conf.in @@ -0,0 +1,35 @@ +# This derives from the global common config +lxc.include = @LXCTEMPLATECONFIG@/common.conf + +# Allow for 6 tty devices by default +lxc.tty.max = 6 + +# Set $VIRTUALIZATION so runit doesn't try to mount filesystems or start udevd +lxc.environment=VIRTUALIZATION=lxc + +# Set the halt/stop signals +lxc.signal.halt=SIGCONT + + +# Uncomment to disable creating tty devices subdirectory in /dev +# lxc.tty.dir = + +# Capabilities +# Uncomment these if you don't run anything that needs the capability, and +# would like the container to run with less privilege. +# +# Dropping sys_admin disables container root from doing a lot of things +# that could be bad like re-mounting lxc fstab entries rw for example, +# but also disables some useful things like being able to nfs mount, and +# things that are already namespaced with ns_capable() kernel checks, like +# hostname(1). +# lxc.cap.drop = sys_admin +# lxc.cap.drop = net_raw # breaks dhcp/ping +# lxc.cap.drop = setgid # breaks login (initgroups/setgroups) +# lxc.cap.drop = dac_read_search # breaks login (pam unix_chkpwd) +# lxc.cap.drop = setuid # breaks sshd,nfs statd +# lxc.cap.drop = audit_control # breaks sshd (set_loginuid failed) +# lxc.cap.drop = audit_write +# lxc.cap.drop = setpcap # big big login delays in Fedora 20 systemd +# +lxc.cap.drop = setfcap sys_nice sys_pacct sys_rawio diff --git a/config/voidlinux.userns.conf.in b/config/voidlinux.userns.conf.in new file mode 100644 index 0000000..f319bc5 --- /dev/null +++ b/config/voidlinux.userns.conf.in @@ -0,0 +1,8 @@ +# This derives from the global userns config +lxc.include = @LXCTEMPLATECONFIG@/userns.conf + +# Set $VIRTUALIZATION so runit doesn't try to mount filesystems or start udevd +lxc.environment=VIRTUALIZATION=lxc + +# Set the halt/stop signals +lxc.signal.halt=SIGCONT diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..c03d752 --- /dev/null +++ b/configure.ac @@ -0,0 +1,222 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_INIT([lxc-templates], [1.0.0]) +AM_INIT_AUTOMAKE + +# We need pkg-config +PKG_PROG_PKG_CONFIG + +AC_CONFIG_SRCDIR([configure.ac]) +# AC_CONFIG_AUX_DIR([config]) +# AC_CONFIG_HEADERS([src/config.h]) +# AM_INIT_AUTOMAKE([-Wall -Werror -Wno-portability subdir-objects]) +AC_CANONICAL_HOST +# AM_PROG_CC_C_O +AC_GNU_SOURCE + +# Detect the distribution. This is used for the default configuration and +# for some distro-specific build options. +AC_MSG_CHECKING([host distribution]) +AC_ARG_WITH(distro, AS_HELP_STRING([--with-distro=DISTRO], [Specify the Linux distribution to target: One of redhat, oracle, centos, fedora, suse, gentoo, debian, arch, slackware, plamo, paldo, openmandriva, pardus, sparclinux, altlinux.])) +if type lsb_release >/dev/null 2>&1 && test "z$with_distro" = "z"; then + with_distro=`lsb_release -is` +fi +if test "z$with_distro" = "z"; then + AC_CHECK_FILE(/etc/redhat-release,with_distro="redhat") + AC_CHECK_FILE(/etc/oracle-release,with_distro="oracle") + AC_CHECK_FILE(/etc/sparclinux-release,with_distro="sparclinux") + AC_CHECK_FILE(/etc/centos-release,with_distro="centos") + AC_CHECK_FILE(/etc/fedora-release,with_distro="fedora") + AC_CHECK_FILE(/etc/SuSE-release,with_distro="suse") + AC_CHECK_FILE(/etc/gentoo-release,with_distro="gentoo") + AC_CHECK_FILE(/etc/debian_version,with_distro="debian") + AC_CHECK_FILE(/etc/arch-release,with_distro="arch") + AC_CHECK_FILE(/etc/slackware-version,with_distro="slackware") + AC_CHECK_FILE(/etc/plamo-version,with_distro="plamo") + AC_CHECK_FILE(/etc/frugalware-release,with_distro="frugalware") + AC_CHECK_FILE(/etc/mandrakelinux-release, with_distro="openmandriva") + AC_CHECK_FILE(/etc/mandriva-release,with_distro="openmandriva") + AC_CHECK_FILE(/etc/pardus-release,with_distro="pardus") + AC_CHECK_FILE(/etc/altlinux-release,with_distro="altlinux") + AC_CHECK_FILE(/etc/pld-release,with_distro="pld") +fi +with_distro=`echo ${with_distro} | tr '[[:upper:]]' '[[:lower:]]'` + +if test "z$with_distro" = "zforsparc"; then + with_distro="sparclinux" +fi + +if test "z$with_distro" = "z"; then + with_distro="unknown" +fi +case $with_distro in + ubuntu|raspbian) + distrosysconf="$sysconfdir/default" + ;; + redhat|centos|fedora|oracle|oracleserver|sparclinux|altlinux|suse|opensuse*|plamo|pld) + distrosysconf="$sysconfdir/sysconfig" + ;; + *) + distrosysconf="$sysconfdir/default" + ;; +esac +AC_MSG_RESULT([$with_distro]) +AM_CONDITIONAL([HAVE_DEBIAN], [test x"$with_distro" = "xdebian" -o x"$with_distro" = "xubuntu" -o x"$with_distro" = "xraspbian"]) +AM_CONDITIONAL([DISTRO_UBUNTU], [test "x$with_distro" = "xubuntu"]) + +AC_CONFIG_LINKS([config/etc/default.conf:config/etc/${distroconf}]) + +# Not in older autoconf versions +# AS_VAR_COPY(DEST, SOURCE) +# ------------------------- +# Set the polymorphic shell variable DEST to the contents of the polymorphic +# shell variable SOURCE. +m4_ifdef([AS_VAR_COPY], [], +[AC_DEFUN([AS_VAR_COPY], + [AS_LITERAL_IF([$1[]$2], [$1=$$2], [eval $1=\$$2])]) +]) + +dnl PKG_CHECK_VAR was introduced with pkg-config 0.28 +m4_ifdef([PKG_CHECK_VAR], [], +[AC_DEFUN([PKG_CHECK_VAR], + [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl + AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + _PKG_CONFIG([$1], [variable="][$3]["], [$2]) + AS_VAR_COPY([$1], [pkg_cv_][$1]) + AS_VAR_IF([$1], [""], [$5], [$4])dnl + ])# PKG_CHECK_VAR +]) + +# Allow overriding the default runtime dir (/run) +AC_ARG_WITH([runtime-path], + [AC_HELP_STRING( + [--with-runtime-path=dir], + [runtime directory (default: /run)] + )], [], [with_runtime_path=['/run']]) + +# LXC container path, where the containers are actually stored +# This is overridden by an entry in the file called LXCCONF +# (i.e. /etc/lxc/lxc.conf) +AC_ARG_WITH([config-path], + [AC_HELP_STRING( + [--with-config-path=dir], + [lxc configuration repository path] + )], [], [with_config_path=['${localstatedir}/lib/lxc']]) + +# The path of the global lxc configuration file. +AC_ARG_WITH([global-conf], + [AC_HELP_STRING( + [--with-global-conf=dir], + [global lxc configuration file] + )], [], [with_global_conf=['${sysconfdir}/lxc/lxc.conf']]) + +# Rootfs path, where the container mount structure is assembled +AC_ARG_WITH([rootfs-path], + [AC_HELP_STRING( + [--with-rootfs-path=dir], + [lxc rootfs mount point] + )], [], [with_rootfs_path=['${libdir}/lxc/rootfs']]) + +# Expand some useful variables +AS_AC_EXPAND(PREFIX, "$prefix") +AS_AC_EXPAND(LIBDIR, "$libdir") +AS_AC_EXPAND(BINDIR, "$bindir") +AS_AC_EXPAND(SBINDIR, "$sbindir") +AS_AC_EXPAND(LIBEXECDIR, "$libexecdir") +AS_AC_EXPAND(INCLUDEDIR, "$includedir") +AS_AC_EXPAND(SYSCONFDIR, "$sysconfdir") +AS_AC_EXPAND(LXC_DEFAULT_CONFIG, "$sysconfdir/lxc/default.conf") +AS_AC_EXPAND(DATADIR, "$datadir") +AS_AC_EXPAND(LOCALSTATEDIR, "$localstatedir") +AS_AC_EXPAND(LXC_GENERATE_DATE, "$(date --utc --date=@${SOURCE_DATE_EPOCH:-$(date +%s)} '+%Y-%m-%d')") +AS_AC_EXPAND(LXCPATH, "$with_config_path") +AS_AC_EXPAND(LXC_GLOBAL_CONF, "$with_global_conf") +AS_AC_EXPAND(LXC_USERNIC_CONF, "$with_usernic_conf") +AS_AC_EXPAND(LXC_USERNIC_DB, "$with_usernic_db") +AS_AC_EXPAND(LXC_DISTRO_SYSCONF, "$distrosysconf") +AS_AC_EXPAND(LXCROOTFSMOUNT, "$with_rootfs_path") +AS_AC_EXPAND(LXCTEMPLATEDIR, "$datadir/lxc/templates") +AS_AC_EXPAND(LXCTEMPLATECONFIG, "$datadir/lxc/config") +AS_AC_EXPAND(LXCHOOKDIR, "$datadir/lxc/hooks") +AS_AC_EXPAND(LXCBINHOOKDIR, "$libexecdir/lxc/hooks") +AS_AC_EXPAND(LXCINITDIR, "$libexecdir") +AS_AC_EXPAND(RUNTIME_PATH, "$with_runtime_path") + +# Check for some standard binaries +AC_PROG_SED + +# Files requiring some variable expansion +AC_CONFIG_FILES([ + Makefile + + config/Makefile + config/alpine.common.conf + config/alpine.userns.conf + config/archlinux.common.conf + config/archlinux.userns.conf + config/centos.common.conf + config/centos.userns.conf + config/debian.common.conf + config/debian.userns.conf + config/fedora.common.conf + config/fedora.userns.conf + config/gentoo.common.conf + config/gentoo.moresecure.conf + config/gentoo.userns.conf + config/opensuse.common.conf + config/opensuse.userns.conf + config/oracle.common.conf + config/oracle.userns.conf + config/plamo.common.conf + config/plamo.userns.conf + config/slackware.common.conf + config/slackware.userns.conf + config/ubuntu-cloud.common.conf + config/ubuntu-cloud.lucid.conf + config/ubuntu-cloud.userns.conf + config/ubuntu.common.conf + config/ubuntu.lucid.conf + config/ubuntu.userns.conf + config/openwrt.common.conf + config/sparclinux.common.conf + config/sparclinux.userns.conf + config/voidlinux.common.conf + config/voidlinux.userns.conf + config/sabayon.common.conf + config/sabayon.userns.conf + + templates/Makefile + templates/lxc-alpine + templates/lxc-altlinux + templates/lxc-archlinux + templates/lxc-busybox + templates/lxc-centos + templates/lxc-cirros + templates/lxc-debian + templates/lxc-fedora + templates/lxc-fedora-legacy + templates/lxc-gentoo + templates/lxc-openmandriva + templates/lxc-opensuse + templates/lxc-oracle + templates/lxc-plamo + templates/lxc-pld + templates/lxc-slackware + templates/lxc-sshd + templates/lxc-ubuntu + templates/lxc-ubuntu-cloud + templates/lxc-sparclinux + templates/lxc-voidlinux + templates/lxc-sabayon +]) +AC_CONFIG_COMMANDS([default],[[]],[[]]) +AC_OUTPUT + +# Configuration overview +cat << EOF + +---------------------------- +Environment: + - distribution: $with_distro +EOF diff --git a/templates/Makefile.am b/templates/Makefile.am new file mode 100644 index 0000000..508fee3 --- /dev/null +++ b/templates/Makefile.am @@ -0,0 +1,25 @@ +templatesdir=@LXCTEMPLATEDIR@ + +templates_SCRIPTS = \ + lxc-alpine \ + lxc-altlinux \ + lxc-archlinux \ + lxc-busybox \ + lxc-centos \ + lxc-cirros \ + lxc-debian \ + lxc-fedora \ + lxc-fedora-legacy \ + lxc-gentoo \ + lxc-openmandriva \ + lxc-opensuse \ + lxc-oracle \ + lxc-plamo \ + lxc-pld \ + lxc-slackware \ + lxc-sshd \ + lxc-ubuntu \ + lxc-ubuntu-cloud \ + lxc-sparclinux \ + lxc-voidlinux \ + lxc-sabayon diff --git a/templates/lxc-alpine.in b/templates/lxc-alpine.in new file mode 100644 index 0000000..174c368 --- /dev/null +++ b/templates/lxc-alpine.in @@ -0,0 +1,516 @@ +#!/bin/sh +# vim: set ts=4: + +# Exit on error and treat unset variables as an error. +set -eu + +# +# LXC template for Alpine Linux 3+ +# + +# Note: Do not replace tabs with spaces, it would break heredocs! + +# Authors: +# Jakub Jirutka + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +#=========================== Constants ============================# + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +readonly LOCAL_STATE_DIR='@LOCALSTATEDIR@' +readonly LXC_TEMPLATE_CONFIG='@LXCTEMPLATECONFIG@' +readonly LXC_CACHE_DIR="${LXC_CACHE_PATH:-"$LOCAL_STATE_DIR/cache/lxc"}/alpine" + +# SHA256 checksums of GPG keys for APK. +readonly APK_KEYS_SHA256="\ +9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4 alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub +2adcf7ce224f476330b5360ca5edb92fd0bf91c92d83292ed028d7c4e26333ab alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub +ebf31683b56410ecc4c00acd9f6e2839e237a3b62b5ae7ef686705c7ba0396a9 alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub +1bb2a846c0ea4ca9d0e7862f970863857fc33c32f5506098c636a62a726a847b alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub +12f899e55a7691225603d6fb3324940fc51cd7f133e7ead788663c2b7eecb00c alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub +73867d92083f2f8ab899a26ccda7ef63dfaa0032a938620eda605558958a8041 alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub +9a4cd858d9710963848e6d5f555325dc199d1c952b01cf6e64da2c15deedbd97 alpine-devel@lists.alpinelinux.org-58cbb476.rsa.pub +780b3ed41786772cbc7b68136546fa3f897f28a23b30c72dde6225319c44cfff alpine-devel@lists.alpinelinux.org-58e4f17d.rsa.pub" + +readonly APK_KEYS_URI='http://alpinelinux.org/keys' +readonly DEFAULT_MIRROR_URL='http://dl-cdn.alpinelinux.org/alpine' + +: ${APK_KEYS_DIR:=/etc/apk/keys} +if ! ls "$APK_KEYS_DIR"/alpine* >/dev/null 2>&1; then + APK_KEYS_DIR="$LXC_CACHE_DIR/bootstrap/keys" +fi +readonly APK_KEYS_DIR + +: ${APK:=$(command -v apk || true)} +if [ ! -x "$APK" ]; then + APK="$LXC_CACHE_DIR/bootstrap/sbin/apk.static" +fi +readonly APK + + +#======================== Helper Functions ========================# + +usage() { + cat <<-EOF + Template specific options can be passed to lxc-create after a '--' like this: + + lxc-create --name=NAME [lxc-create-options] -- [template-options] [PKG...] + + PKG Additional APK package(s) to install into the container. + + Template options: + -a ARCH, --arch=ARCH The container architecture (e.g. x86, x86_64); defaults + to the host arch. + -d, --debug Run this script in a debug mode (set -x and wget w/o -q). + -F, --flush-cache Remove cached files before build. + -m URL --mirror=URL The Alpine mirror to use; defaults to $DEFAULT_MIRROR_URL. + -r VER, --release=VER The Alpine release branch to install; default is the + latest stable. + + Environment variables: + APK The apk-tools binary to use when building rootfs. If not set + or not executable and apk is not on PATH, then the script + will download the latest apk-tools-static. + APK_KEYS_DIR Path to directory with GPG keys for APK. If not set and + /etc/apk/keys does not contain alpine keys, then the script + will download the keys from ${APK_KEYS_URI}. + LXC_CACHE_PATH Path to the cache directory where to store bootstrap files + and APK packages. + EOF +} + +die() { + local retval=$1; shift + + printf 'ERROR: %s\n' "$@" 1>&2 + exit $retval +} + +einfo() { + printf "\n==> $1\n" +} + +fetch() { + if [ "$DEBUG" = 'yes' ]; then + wget -T 10 -O - $@ + else + wget -T 10 -O - -q $@ + fi +} + +latest_release_branch() { + local arch="$1" + local branch=$(fetch "$MIRROR_URL/latest-stable/releases/$arch/latest-releases.yaml" \ + | sed -En 's/^[ \t]*branch: (.*)$/\1/p' \ + | head -n 1) + [ -n "$branch" ] && echo "$branch" +} + +parse_arch() { + case "$1" in + x86 | i[3-6]86) echo 'x86';; + x86_64 | amd64) echo 'x86_64';; + aarch64 | arm64) echo 'aarch64';; + armv7) echo 'armv7';; + arm*) echo 'armhf';; + ppc64le) echo 'ppc64le';; + *) return 1;; + esac +} + +run_exclusively() { + local lock_name="$1" + local timeout=$2 + shift 2 + + mkdir -p "$LOCAL_STATE_DIR/lock/subsys" + + local retval + { + echo -n "Obtaining an exclusive lock..." + if ! flock -x 9; then + echo ' failed.' + return 1 + fi + echo ' done' + + "$@"; retval=$? + } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-alpine-$lock_name" + + return $retval +} + + +#============================ Bootstrap ===========================# + +bootstrap() { + if [ "$FLUSH_CACHE" = 'yes' ] && [ -d "$LXC_CACHE_DIR/bootstrap" ]; then + einfo 'Cleaning cached bootstrap files' + rm -Rf "$LXC_CACHE_DIR/bootstrap" + fi + + einfo 'Fetching and/or verifying APK keys' + fetch_apk_keys "$APK_KEYS_DIR" + + if [ ! -x "$APK" ]; then + einfo 'Fetching apk-tools static binary' + + local host_arch=$(parse_arch $(uname -m)) + fetch_apk_static "$LXC_CACHE_DIR/bootstrap" "$host_arch" + fi +} + +fetch_apk_keys() { + local dest="$1" + local line keyname + + mkdir -p "$dest" + cd "$dest" + + echo "$APK_KEYS_SHA256" | while read -r line; do + keyname="${line##* }" + if [ ! -s "$keyname" ]; then + fetch "$APK_KEYS_URI/$keyname" > "$keyname" + fi + echo "$line" | sha256sum -c - + done || exit 2 + + cd - >/dev/null +} + +fetch_apk_static() { + local dest="$1" + local arch="$2" + local pkg_name='apk-tools-static' + + mkdir -p "$dest" + + local pkg_ver=$(fetch "$MIRROR_URL/latest-stable/main/$arch/APKINDEX.tar.gz" \ + | tar -xzO APKINDEX \ + | sed -n "/P:${pkg_name}/,/^$/ s/V:\(.*\)$/\1/p") + + [ -n "$pkg_ver" ] || die 2 "Cannot find a version of $pkg_name in APKINDEX" + + fetch "$MIRROR_URL/latest-stable/main/$arch/${pkg_name}-${pkg_ver}.apk" \ + | tar -xz -C "$dest" sbin/ # --extract --gzip --directory + + [ -s "$dest/sbin/apk.static" ] || die 2 'apk.static not found' + + local keyname=$(echo "$dest"/sbin/apk.static.*.pub | sed 's/.*\.SIGN\.RSA\.//') + openssl dgst -sha1 \ + -verify "$APK_KEYS_DIR/$keyname" \ + -signature "$dest/sbin/apk.static.SIGN.RSA.$keyname" \ + "$dest/sbin/apk.static" \ + || die 2 'Signature verification for apk.static failed' + + # Note: apk doesn't return 0 for --version + local out="$("$dest"/sbin/apk.static --version)" + echo "$out" + + [ "${out%% *}" = 'apk-tools' ] || die 3 'apk.static --version failed' +} + + +#============================ Install ============================# + +install() { + local dest="$1" + local arch="$2" + local branch="$3" + local extra_packages="$4" + local apk_cache="$LXC_CACHE_DIR/apk/$arch" + + if [ "$FLUSH_CACHE" = 'yes' ] && [ -d "$apk_cache" ]; then + einfo "Cleaning cached APK packages for $arch" + rm -Rf "$apk_cache" + fi + mkdir -p "$apk_cache" + + einfo "Installing Alpine Linux in $dest" + cd "$dest" + + mkdir -p etc/apk + ln -s "$apk_cache" etc/apk/cache + + local repo; for repo in main community; do + echo "$MIRROR_URL/$branch/$repo" >> etc/apk/repositories + done + + install_packages "$arch" "alpine-base $extra_packages" + make_dev_nodes + setup_inittab + setup_hosts + setup_network + setup_services + + chroot . /bin/true \ + || die 3 'Failed to execute /bin/true in chroot, the builded rootfs is broken!' + + rm etc/apk/cache + cd - >/dev/null +} + +install_packages() { + local arch="$1" + local packages="$2" + + $APK --arch="$arch" --root=. --keys-dir="$APK_KEYS_DIR" \ + --update-cache --initdb add $packages +} + +make_dev_nodes() { + mkdir -p -m 755 dev/pts + mkdir -p -m 1777 dev/shm + + mknod -m 666 dev/zero c 1 5 + mknod -m 666 dev/full c 1 7 + mknod -m 666 dev/random c 1 8 + mknod -m 666 dev/urandom c 1 9 + + local i; for i in $(seq 0 4); do + mknod -m 620 dev/tty$i c 4 $i + chown 0:5 dev/tty$i # root:tty + done + + mknod -m 666 dev/tty c 5 0 + chown 0:5 dev/tty # root:tty + mknod -m 620 dev/console c 5 1 + mknod -m 666 dev/ptmx c 5 2 + chown 0:5 dev/ptmx # root:tty +} + +setup_inittab() { + # Remove unwanted ttys. + sed -i '/^tty[5-9]\:\:.*$/d' etc/inittab + + cat <<-EOF >> etc/inittab + # Main LXC console console + ::respawn:/sbin/getty 38400 console + EOF +} + +setup_hosts() { + # This runscript injects localhost entries with the current hostname + # into /etc/hosts. + cat <<'EOF' > etc/init.d/hosts +#!/sbin/openrc-run + +start() { + local start_tag='# begin generated' + local end_tag='# end generated' + + local content=$( + cat <<-EOF + $start_tag by /etc/init.d/hosts + 127.0.0.1 $(hostname).local $(hostname) localhost + ::1 $(hostname).local $(hostname) localhost + $end_tag + EOF + ) + + if grep -q "^${start_tag}" /etc/hosts; then + # escape \n, busybox sed doesn't like them + content=${content//$'\n'/\\$'\n'} + + sed -ni "/^${start_tag}/ { + a\\${content} + # read and discard next line and repeat until $end_tag or EOF + :a; n; /^${end_tag}/!ba; n + }; p" /etc/hosts + else + printf "$content" >> /etc/hosts + fi +} +EOF + chmod +x etc/init.d/hosts + + # Wipe it, will be generated by the above runscript. + echo -n > etc/hosts +} + +setup_network() { + # Note: loopback is automatically started by LXC. + cat <<-EOF > etc/network/interfaces + auto eth0 + iface eth0 inet dhcp + hostname \$(hostname) + EOF +} + +setup_services() { + local svc_name + + # Specify the LXC subsystem. + sed -i 's/^#*rc_sys=.*/rc_sys="lxc"/' etc/rc.conf + + # boot runlevel + for svc_name in bootmisc hosts syslog; do + ln -s /etc/init.d/$svc_name etc/runlevels/boot/$svc_name + done + + # default runlevel + for svc_name in networking cron crond; do + # issue 1164: alpine renamed cron to crond + # Use the one that exists. + if [ -e etc/init.d/$svc_name ]; then + ln -s /etc/init.d/$svc_name etc/runlevels/default/$svc_name + fi + done +} + + +#=========================== Configure ===========================# + +configure_container() { + local config="$1" + local hostname="$2" + local arch="$3" + + cat <<-EOF >> "$config" + + # Specify container architecture. + lxc.arch = $arch + + # Set hostname. + lxc.uts.name = $hostname + + # If something doesn't work, try to comment this out. + # Dropping sys_admin disables container root from doing a lot of things + # that could be bad like re-mounting lxc fstab entries rw for example, + # but also disables some useful things like being able to nfs mount, and + # things that are already namespaced with ns_capable() kernel checks, like + # hostname(1). + lxc.cap.drop = sys_admin + + # Comment this out if you have to debug processes by tracing. + lxc.cap.drop = sys_ptrace + + # Include common configuration. + lxc.include = $LXC_TEMPLATE_CONFIG/alpine.common.conf + EOF +} + + +#============================= Main ==============================# + +if [ "$(id -u)" != "0" ]; then + die 1 "This script must be run as 'root'" +fi + +# Parse command options. +options=$(getopt -o a:dFm:n:p:r:h -l arch:,debug,flush-cache,mirror:,name:,\ +path:,release:,rootfs:,help,mapped-uid:,mapped-gid: -- "$@") +eval set -- "$options" + +# Clean variables and set defaults. +arch="$(uname -m)" +debug='no' +flush_cache='no' +mirror_url= +name= +path= +release= +rootfs= + +# Process command options. +while [ $# -gt 0 ]; do + case $1 in + -a | --arch) + arch=$2; shift 2 + ;; + -d | --debug) + debug='yes'; shift 1 + ;; + -F | --flush-cache) + flush_cache='yes'; shift 1 + ;; + -m | --mirror) + mirror_url=$2; shift 2 + ;; + -n | --name) + name=$2; shift 2 + ;; + -p | --path) + path=$2; shift 2 + ;; + -r | --release) + release=$2; shift 2 + ;; + --rootfs) + rootfs=$2; shift 2 + ;; + -h | --help) + usage; exit 0 + ;; + --) + shift; break + ;; + --mapped-[ug]id) + die 1 "This template can't be used for unprivileged containers." \ + 'You may want to try the "download" template instead.' + ;; + *) + echo "Unknown option: $1" 1>&2 + usage; exit 1 + ;; + esac +done + +extra_packages="$@" + +[ "$debug" = 'yes' ] && set -x + +# Set global variables. +readonly DEBUG="$debug" +readonly FLUSH_CACHE="$flush_cache" +readonly MIRROR_URL="${mirror_url:-$DEFAULT_MIRROR_URL}" + +# Validate options. +[ -n "$name" ] || die 1 'Missing required option --name' +[ -n "$path" ] || die 1 'Missing required option --path' + +if [ -z "$rootfs" ] && [ -f "$path/config" ]; then + rootfs="$(sed -nE 's/^lxc.rootfs.path\s*=\s*(.*)$/\1/p' "$path/config")" +fi +if [ -z "$rootfs" ]; then + rootfs="$path/rootfs" +fi + +arch=$(parse_arch "$arch") \ + || die 1 "Unsupported architecture: $arch" + +if [ -z "$release" ]; then + release=$(latest_release_branch "$arch") \ + || die 2 'Failed to resolve Alpine last release branch' +fi + +# Here we go! +run_exclusively 'bootstrap' 10 bootstrap +run_exclusively "$arch" 30 install "$rootfs" "$arch" "$release" "$extra_packages" +configure_container "$path/config" "$name" "$arch" + +einfo "Container's rootfs and config have been created" +cat <<-EOF + Edit the config file $path/config to check/enable networking setup. + The installed system is preconfigured for a loopback and single network + interface configured via DHCP. + + To start the container, run "lxc-start -n $name". + The root password is not set; to enter the container run "lxc-attach -n $name". +EOF diff --git a/templates/lxc-altlinux.in b/templates/lxc-altlinux.in new file mode 100644 index 0000000..d526fa0 --- /dev/null +++ b/templates/lxc-altlinux.in @@ -0,0 +1,498 @@ +#!/bin/bash + +# +# template script for generating altlinux container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# Alexey Shabalin + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +#Configurations +arch=$(uname -m) +cache_base=@LOCALSTATEDIR@/cache/lxc/altlinux/$arch +default_path=@LXCPATH@ +default_profile=default +profile_dir=/etc/lxc/profiles +lxc_network_type=veth +lxc_network_link=virbr0 + +# is this altlinux? +[ -f /etc/altlinux-release ] && is_altlinux=true + +configure_altlinux() +{ + + # disable selinux in altlinux + mkdir -p $rootfs_path/selinux + echo 0 > $rootfs_path/selinux/enforce + + mkdir -p ${rootfs_path}/etc/net/ifaces/eth0 + cat < ${rootfs_path}/etc/net/ifaces/eth0/options +BOOTPROTO=${BOOTPROTO} +ONBOOT=yes +NM_CONTROLLED=yes +TYPE=eth +EOF + +if [ ${BOOTPROTO} != "dhcp" ]; then + # ip address + cat < ${rootfs_path}/etc/net/ifaces/eth0/ipv4address +${ipv4} +EOF + + cat < ${rootfs_path}/etc/net/ifaces/eth0/ipv4route +${gw} +EOF + + cat < ${rootfs_path}/etc/net/ifaces/eth0/resolv.conf +nameserver ${dns} +EOF + + cat < ${rootfs_path}/etc/net/ifaces/eth0/ipv6address +${ipv6} +EOF + + cat < ${rootfs_path}/etc/net/ifaces/eth0/ipv6route +${gw6} +EOF + +fi + + # set the hostname + cat < ${rootfs_path}/etc/sysconfig/network +NETWORKING=yes +CONFMETHOD=etcnet +HOSTNAME=${UTSNAME} +RESOLV_MODS=yes +EOF + + # set minimal hosts + cat < $rootfs_path/etc/hosts +127.0.0.1 localhost.localdomain localhost $name +EOF + # Allow to login at virsh console. loginuid.so doen't work in the absence of auditd. +# sed -i 's/^.*loginuid.so.*$/\#&/' ${rootfs_path}/etc/pam.d/common-login + + # Allow root to login at virsh console + echo "pts/0" >> ${rootfs_path}/etc/securetty + echo "console" >> ${rootfs_path}/etc/securetty + + # Enable services + for service in network syslogd random NetworkManager + do + chroot ${rootfs_path} chkconfig $service --list &>/dev/null && chroot ${rootfs_path} chkconfig $service on || true + # For systemd + chroot ${rootfs_path} systemctl -q enable $service &>/dev/null|| true + done + # Disable services + for service in rawdevices fbsetfont + do + chroot ${rootfs_path} chkconfig $service --list &>/dev/null && chroot ${rootfs_path} chkconfig $service off || true + chroot ${rootfs_path} systemctl -q disable $service &>/dev/null || true + done + + subst 's/^\([3-9]\+:[0-9]\+:respawn:\/sbin\/mingetty.*\)/#\1/' ${rootfs_path}/etc/inittab + echo "c1:2345:respawn:/sbin/mingetty --noclear console" >> ${rootfs_path}/etc/inittab + + [ -f "${rootfs_path}/etc/syslog.conf" ] && \ + subst 's,\/dev\/tty12,/var/log/syslog/console,' ${rootfs_path}/etc/syslog.conf + + dev_path="${rootfs_path}/dev" + rm -rf ${dev_path} + mkdir -p ${dev_path} + mknod -m 666 ${dev_path}/null c 1 3 + mknod -m 666 ${dev_path}/zero c 1 5 + mknod -m 644 ${dev_path}/random c 1 8 + mknod -m 644 ${dev_path}/urandom c 1 9 + mkdir -m 755 ${dev_path}/pts + mkdir -m 1777 ${dev_path}/shm + mknod -m 666 ${dev_path}/tty c 5 0 + chown root:tty ${dev_path}/tty + mknod -m 600 ${dev_path}/tty0 c 4 0 + mknod -m 600 ${dev_path}/tty1 c 4 1 + mknod -m 600 ${dev_path}/tty2 c 4 2 + mknod -m 600 ${dev_path}/tty3 c 4 3 + mknod -m 600 ${dev_path}/tty4 c 4 4 + mknod -m 600 ${dev_path}/console c 5 1 + mknod -m 666 ${dev_path}/full c 1 7 + mknod -m 600 ${dev_path}/initctl p + mknod -m 666 ${dev_path}/ptmx c 5 2 + chown root:tty ${dev_path}/ptmx + ln -s /proc/self/fd ${dev_path}/fd + ln -s /proc/kcore ${dev_path}/core + mkdir -m 755 ${dev_path}/mapper + mknod -m 600 ${dev_path}/mapper/control c 10 236 + mkdir -m 755 ${dev_path}/net + mknod -m 666 ${dev_path}/net/tun c 10 200 + + if [ -n "${root_password}" ]; then + echo "setting root passwd to $root_password" + echo "root:$root_password" | chroot $rootfs_path chpasswd + fi + + return 0 +} + +download_altlinux() +{ + + if [ -z "$aptconfver" ]; then + case "$release" in + sisyphus) + aptconfver=apt-conf-sisyphus ;; + *) + aptconfver=apt-conf-branch ;; + esac + fi + + # check the mini altlinux was not already downloaded + INSTALL_ROOT=$cache/partial + mkdir -p $INSTALL_ROOT + if [ $? -ne 0 ]; then + echo "Failed to create '$INSTALL_ROOT' directory" + return 1 + fi + + # download a mini altlinux into a cache + echo "Downloading altlinux minimal ..." + APT_GET="apt-get -o RPM::RootDir=$INSTALL_ROOT -y" + PKG_LIST="$(grep -hs '^[^#]' "$profile_dir/$profile")" + # if no configuration file $profile -- fall back to default list of packages + [ -z "$PKG_LIST" ] && PKG_LIST="interactivesystem apt $aptconfver etcnet-full openssh-server systemd-sysvinit systemd-units systemd NetworkManager-daemon" + + mkdir -p $INSTALL_ROOT/var/lib/rpm + rpm --root $INSTALL_ROOT --initdb + + # some scripts want to have /dev/null at least + dev_path="$INSTALL_ROOT/dev" + if [ ! -c "${dev_path}/null" ]; then + mkdir -p "${dev_path}" + mknod -m 666 "${dev_path}/null" c 1 3 + fi + + $APT_GET install $PKG_LIST + + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + + mv "$INSTALL_ROOT" "$cache/rootfs" + echo "Download complete." + + return 0 +} + +copy_altlinux() +{ + + # make a local copy of the minialtlinux + echo -n "Copying rootfs to $rootfs_path ..." + #cp -a $cache/rootfs-$arch $rootfs_path || return 1 + # i prefer rsync (no reason really) + mkdir -p $rootfs_path + rsync -SHaAX $cache/rootfs/ $rootfs_path/ + return 0 +} + +update_altlinux() +{ + chroot $cache/rootfs apt-get update + chroot $cache/rootfs apt-get -y dist-upgrade +} + +install_altlinux() +{ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ + ( + flock -x 9 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + echo "Checking cache download in $cache/rootfs ... " + if [ ! -e "$cache/rootfs" ]; then + download_altlinux + if [ $? -ne 0 ]; then + echo "Failed to download 'altlinux base'" + return 1 + fi + else + echo "Cache found. Updating..." + update_altlinux + if [ $? -ne 0 ]; then + echo "Failed to update 'altlinux base', continuing with last known good cache" + else + echo "Update finished" + fi + fi + + echo "Copy $cache/rootfs to $rootfs_path ... " + copy_altlinux + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + return 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-altlinux + + return $? +} + +copy_configuration() +{ + + mkdir -p $config_path + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs_path" >> $config_path/config + cat <> $config_path/config +lxc.uts.name = $name +lxc.tty.max = 4 +lxc.pty.max = 1024 +lxc.cap.drop = sys_module mac_admin mac_override sys_time + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +#networking +#lxc.net.0.type = $lxc_network_type +#lxc.net.0.flags = up +#lxc.net.0.link = $lxc_network_link +#lxc.net.0.name = veth0 +#lxc.net.0.mtu = 1500 +EOF +if [ ! -z ${ipv4} ]; then + cat <> $config_path/config +lxc.net.0.ipv4.address = $ipv4 +EOF +fi +if [ ! -z ${gw} ]; then + cat <> $config_path/config +lxc.net.0.ipv4.gateway = $gw +EOF +fi +if [ ! -z ${ipv6} ]; then + cat <> $config_path/config +lxc.net.0.ipv6.address = $ipv6 +EOF +fi +if [ ! -z ${gw6} ]; then + cat <> $config_path/config +lxc.net.0.ipv6.gateway = $gw6 +EOF +fi + cat <> $config_path/config +#cgroups +lxc.cgroup.devices.deny = a +# /dev/null and zero +lxc.cgroup.devices.allow = c 1:3 rwm +lxc.cgroup.devices.allow = c 1:5 rwm +# consoles +lxc.cgroup.devices.allow = c 5:1 rwm +lxc.cgroup.devices.allow = c 5:0 rwm +lxc.cgroup.devices.allow = c 4:0 rwm +lxc.cgroup.devices.allow = c 4:1 rwm +# /dev/{,u}random +lxc.cgroup.devices.allow = c 1:9 rwm +lxc.cgroup.devices.allow = c 1:8 rwm +lxc.cgroup.devices.allow = c 136:* rwm +lxc.cgroup.devices.allow = c 5:2 rwm +# rtc +lxc.cgroup.devices.allow = c 10:135 rwm + +lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed +EOF + + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +clean() +{ + + if [ ! -e $cache ]; then + exit 0 + fi + + # lock, so we won't purge while someone is creating a repository + ( + flock -x 9 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + + echo -n "Purging the download cache for ALTLinux-$release..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-altlinux +} + +usage() +{ + cat < + [-p|--path=] [-c|--clean] [-R|--release=] + [-4|--ipv4=] [-6|--ipv6=] + [-g|--gw=] [-d|--dns=] + [-P|--profile=] [--rootfs=] + [-a|--apt-conf=] + [-A|--arch=] + [-h|--help] +Mandatory args: + -n,--name container name, used to as an identifier for that container from now on +Optional args: + -p,--path path to where the container rootfs will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case + -c,--clean clean the cache + -R,--release ALTLinux release for the new container. if the host is ALTLinux, then it will defaultto the host's release. + -4,--ipv4 specify the ipv4 address to assign to the virtualized interface, eg. 192.168.1.123/24 + -6,--ipv6 specify the ipv6 address to assign to the virtualized interface, eg. 2003:db8:1:0:214:1234:fe0b:3596/64 + -g,--gw specify the default gw, eg. 192.168.1.1 + -G,--gw6 specify the default gw, eg. 2003:db8:1:0:214:1234:fe0b:3596 + -d,--dns specify the DNS server, eg. 192.168.1.2 + -a,--apt-conf specify preferred 'apt-conf' package, eg. 'apt-conf-branch' + -P,--profile Profile name is the file name in /etc/lxc/profiles contained packages name for install to cache. + -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64] + ---rootfs rootfs path + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o hp:n:P:cR:4:6:g:d:a: -l help,rootfs:,path:,name:,profile:,clean,release:,ipv4:,ipv6:,gw:,dns:,apt-conf: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs_path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -P|--profile) profile=$2; shift 2;; + -c|--clean) clean=1; shift 1;; + -R|--release) release=$2; shift 2;; + -4|--ipv4) ipv4=$2; shift 2;; + -6|--ipv6) ipv6=$2; shift 2;; + -g|--gw) gw=$2; shift 2;; + -d|--dns) dns=$2; shift 2;; + -a|--apt-conf) aptconfver=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ ! -z "$clean" -a -z "$path" ]; then + clean || exit 1 + exit 0 +fi + +type apt-get >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "'apt-get' command is missing" + exit 1 +fi + +if [ -z "$path" ]; then + path=$default_path +fi + +if [ -z "$profile" ]; then + profile=$default_profile +fi + +if [ -z "$release" ]; then + if [ "$is_altlinux" ]; then + release=$(cat /etc/altlinux-release |awk '/^ALT/ {print $3}') + else + echo "This is not a ALTLinux host and release missing, use -R|--release to specify release" + exit 1 + fi +fi + +if [ -z "$ipv4" -a -z "$ipv6" ]; then + BOOTPROTO="dhcp" +else + BOOTPROTO="static" +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +# check for 'lxc.rootfs.path' passed in through default config by lxc-create +if [ -z "$rootfs_path" ]; then + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config) + else + rootfs_path=$path/rootfs + fi +fi + +config_path=$default_path/$name +cache=$cache_base/$release/$profile + +install_altlinux +if [ $? -ne 0 ]; then + echo "failed to install altlinux" + exit 1 +fi + +configure_altlinux +if [ $? -ne 0 ]; then + echo "failed to configure altlinux for a container" + exit 1 +fi + +copy_configuration +if [ $? -ne 0 ]; then + echo "failed write configuration file" + exit 1 +fi + +if [ ! -z "$clean" ]; then + clean || exit 1 + exit 0 +fi +echo "container rootfs and config created" +echo "network configured as $lxc_network_type in the $lxc_network_link" diff --git a/templates/lxc-archlinux.in b/templates/lxc-archlinux.in new file mode 100644 index 0000000..f8d4ba0 --- /dev/null +++ b/templates/lxc-archlinux.in @@ -0,0 +1,334 @@ +#!/bin/bash + +# +# template script for generating Arch Linux container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# Alexander Vladimirov +# John Lane + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +# defaults +arch=$(uname -m) +default_path="@LXCPATH@" +default_locale="en_US.UTF-8" +pacman_config="/etc/pacman.conf" +common_config="@LXCTEMPLATECONFIG@/common.conf" +shared_config="@LXCTEMPLATECONFIG@/archlinux.common.conf" + +# by default, install 'base' except the kernel +pkg_blacklist="linux" +base_packages=() +for pkg in $(pacman -Sqg base); do + [ "${pkg_blacklist#*$pkg}" = "$pkg_blacklist" ] && base_packages+=($pkg) +done +declare -a additional_packages + +# split comma-separated string into an array +# ${1} - string to split +# ${2} - separator (default is ",") +# ${result} - result value on success +split_string() { + local ifs=${IFS} + IFS="${2:-,}" + read -a result < <(echo "${1}") + IFS=${ifs} + return 0 +} + +[ -f /etc/arch-release ] && is_arch=true + +# Arch-specific preconfiguration for container +configure_arch() { + # on ArchLinux, read defaults from host systemd configuration + if [ "${is_arch}" ]; then + cp -p /etc/locale.conf /etc/locale.gen "${rootfs_path}/etc/" + else + echo "LANG=${default_locale}" > "${rootfs_path}/etc/locale.conf" + if [ -e "${rootfs_path}/etc/locale.gen" ]; then + sed -i 's@^#\(en_US\.UTF-8\)@\1@' "${rootfs_path}/etc/locale.gen" + if [ ! "${default_locale}" = "en_US.UTF-8" ]; then + echo "${default_locale} ${default_locale##*.}" >> \ + "${rootfs_path}/etc/locale.gen" + fi + fi + fi + + # hostname and nameservers + echo "${name}" > "${rootfs_path}/etc/hostname" + + # network configuration + cat > "${rootfs_path}/etc/systemd/network/eth0.network" << EOF +[Match] +Name=eth0 + +[Network] +DHCP=ipv4 +EOF + + # chroot and configure system + arch-chroot "${rootfs_path}" /bin/bash -s << EOF +mkdir /run/lock +locale-gen +# set default boot target +ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target +# override getty@.service for container ttys +sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ + -e 's/After=dev-%i.device/After=/' \ + < /lib/systemd/system/getty\@.service \ + > /etc/systemd/system/getty\@.service +# fix systemd-sysctl service +sed -e 's/^ConditionPathIsReadWrite=\/proc\/sys\/$/ConditionPathIsReadWrite=\/proc\/sys\/net\//' \ + -e 's/^ExecStart=\/usr\/lib\/systemd\/systemd-sysctl$/ExecStart=\/usr\/lib\/systemd\/systemd-sysctl --prefix net/' \ + -i /usr/lib/systemd/system/systemd-sysctl.service +# initialize pacman keyring +pacman-key --init +pacman-key --populate archlinux + +# enable networkd +systemctl enable systemd-networkd +systemctl enable systemd-resolved +ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf +EOF + # enable getty on active ttys + local nttys=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.tty.max" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") + local devttydir=$(cat "${config_path}/config" ${shared_config} ${common_config} | grep "^lxc.tty.dir" | head -n1 | cut -d= -f2 | tr -d "[:blank:]") + local devtty="" + # bind getty instances to /dev//tty* if lxc.tty.dir is set + [ -n "${devttydir}" ] && devtty="${devttydir}-" + if [ ${nttys:-0} -gt 1 ]; then + ( cd "${rootfs_path}/etc/systemd/system/getty.target.wants" + for i in $(seq 1 $nttys); do ln -sf "../getty@.service" "getty@${devtty}tty${i}.service"; done ) + fi + # update securetty to allow console login if devttydir is set + if [ -n "${devttydir}" ]; then + for i in $(seq 1 ${nttys:-1}); do + echo "${devttydir}/tty${i}" >> "${rootfs_path}/etc/securetty" + done + fi + [ -n "${devttydir}" ] && echo "${devttydir}/console" >> "${rootfs_path}/etc/securetty" + # Arch default configuration allows only tty1-6 for login + [ ${nttys:-0} -gt 6 ] && echo \ + "You may want to modify container's /etc/securetty \ + file to allow root logins on tty7 and higher" + return 0 +} + +# write container configuration files +copy_configuration() { + mkdir -p "${config_path}" + local config="${config_path}/config" + echo "lxc.uts.name = ${name}" >> "${config}" + grep -q "^lxc.arch" "${config}" 2>/dev/null \ + || echo "lxc.arch = ${arch}" >> "${config}" + grep -q "^lxc.rootfs.path" "${config}" 2>/dev/null \ + || echo "lxc.rootfs.path = ${rootfs_path}" >> "${config}" + [ -e "${shared_config}" ] \ + && echo "lxc.include = ${shared_config}" >> "${config}" + if [ $? -ne 0 ]; then + echo "Failed to configure container" + return 1 + fi + return 0 +} + +# install packages within container chroot +install_arch() { + [ "${arch}" != "$(uname -m)" ] && different_arch=true + + if [ "${different_arch}" = "true" ]; then + container_pacman_config=$(mktemp) + container_mirrorlist=$(mktemp) + sed -e "s:Architecture =.*:Architecture = ${arch}:g" \ + -e "s:/etc/pacman.d/mirrorlist:${container_mirrorlist}:g" \ + "${pacman_config}" > "${container_pacman_config}" + sed -e "s:\(x86_64\|\$arch\):${arch}:g" \ + /etc/pacman.d/mirrorlist > "${container_mirrorlist}" + + pacman_config="${container_pacman_config}" + fi + + if ! pacstrap -dcGC "${pacman_config}" "${rootfs_path}" \ + ${base_packages[@]}; then + echo "Failed to install container packages" + return 1 + fi + + if [ "${different_arch}" = "true" ]; then + sed -i -e "s:Architecture =.*:Architecture = ${arch}:g" \ + "${rootfs_path}"/etc/pacman.conf + cp "${container_mirrorlist}" "${rootfs_path}"/etc/pacman.d/mirrorlist + rm "${container_pacman_config}" "${container_mirrorlist}" + fi + + [ -d "${rootfs_path}/lib/modules" ] && ldconfig -r "${rootfs_path}" + return 0 +} + +usage() { + cat < [-p|--path=] [-a|--arch=] + [-r|--root_password=] [-P|--packages=] + [-e|--enable_units=unit1,unit2...] [-d|--disable_units=unit1,unit2...] + [-c|--config=] [-h|--help] + +Mandatory args: + -n,--name container name, used to as an identifier for that container from now on +Optional args: + -p,--path path to where the container rootfs will be created (${default_path}) + --rootfs path for actual container rootfs, (${default_path}/rootfs) + -P,--packages preinstall additional packages, comma-separated list + -e,--enable_units enable systemd services, comma-separated list + -d,--disable_units disable systemd services, comma-separated list + -c,--config use specified pacman config when installing container packages + -a,--arch use specified architecture instead of host's architecture + -r,--root_password set container root password + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o hp:P:e:d:n:c:a:r: -l help,rootfs:,path:,packages:,enable_units:,disable_units:,name:,config:,arch:,root_password: -- "${@}") +if [ ${?} -ne 0 ]; then + usage $(basename ${0}) + exit 1 +fi +eval set -- "${options}" + +while true +do + case "${1}" in + -h|--help) usage ${0} && exit 0;; + -p|--path) path=${2}; shift 2;; + -n|--name) name=${2}; shift 2;; + --rootfs) rootfs_path=${2}; shift 2;; + -P|--packages) additional_packages=${2}; shift 2;; + -e|--enable_units) enable_units=${2}; shift 2;; + -d|--disable_units) disable_units=${2}; shift 2;; + -c|--config) pacman_config=${2}; shift 2;; + -a|--arch) arch=${2}; shift 2;; + -r|--root_password) root_passwd=${2}; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ -z "${name}" ]; then + echo "missing required 'name' parameter" + exit 1 +fi + +type pacman >/dev/null 2>&1 +if [ ${?} -ne 0 ]; then + echo "'pacman' command is missing, refer to wiki.archlinux.org for information about installing pacman" + exit 1 +fi + +if [ -z "${path}" ]; then + path="${default_path}/${name}" +fi + +if [ "${EUID}" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -z "$rootfs_path" ]; then + rootfs_path="${path}/rootfs" +fi +config_path="${path}" + +revert() { + echo "Interrupted, cleaning up" + lxc-destroy -n "${name}" + rm -rf "${path}/${name}" + rm -rf "${default_path}/${name}" + exit 1 +} + +trap revert SIGHUP SIGINT SIGTERM + +copy_configuration +if [ ${?} -ne 0 ]; then + echo "failed to write configuration file" + rm -rf "${config_path}" + exit 1 +fi + +if [ ${#additional_packages[@]} -gt 0 ]; then + split_string ${additional_packages} + base_packages+=(${result[@]}) +fi + +mkdir -p "${rootfs_path}" +install_arch +if [ ${?} -ne 0 ]; then + echo "failed to install Arch Linux" + rm -rf "${config_path}" "${path}" + exit 1 +fi + +configure_arch +if [ ${?} -ne 0 ]; then + echo "failed to configure Arch Linux for a container" + rm -rf "${config_path}" "${path}" + exit 1 +fi + +if [ ${#enable_units[@]} -gt 0 ]; then + split_string ${enable_units} + for unit in ${result[@]}; do + [ "${unit##*.}" = "service" ] || unit="${unit}.service" + ln -s "/usr/lib/systemd/system/${unit}" \ + "${rootfs_path}/etc/systemd/system/multi-user.target.wants/" + done +fi + +if [ ${#disable_units[@]} -gt 0 ]; then + split_string ${disable_units} + for unit in ${result[@]}; do + [ "${unit##*.}" = "service" ] || unit="${unit}.service" + ln -s /dev/null "${rootfs_path}/etc/systemd/system/${unit}" + done +fi + +if [ -n "${root_passwd}" ]; then + echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd +fi + +cat << EOF +Arch Linux container ${name} is successfully created! The configuration is +stored in ${config_path}/config. Please refer to https://wiki.archlinux.org for +information about configuring Arch Linux. +EOF diff --git a/templates/lxc-busybox.in b/templates/lxc-busybox.in new file mode 100644 index 0000000..fa7c78f --- /dev/null +++ b/templates/lxc-busybox.in @@ -0,0 +1,484 @@ +#!/bin/bash + +# +# lxc: linux Container library + +# Authors: +# Daniel Lezcano + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +LXC_MAPPED_UID= +LXC_MAPPED_GID= +SSH= + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +am_in_userns() { + [ -e /proc/self/uid_map ] || { echo no; return; } + [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; } + line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map) + [ "$line" = "0 0 4294967295" ] && { echo no; return; } + echo yes +} + +in_userns=0 +[ $(am_in_userns) = "yes" ] && in_userns=1 + +copy_binary() +{ + binary_path=`which $1` + if [ $? -ne 0 ]; then + echo "Unable to find $1 binary on the system" + return 1 + fi + + dir_path="${binary_path%/*}" + echo /{,usr/}{,s}bin | grep $dir_path >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Binary $1 is located at $binary_path and will not be copied" + echo "($dir_path not supported)" + return 1 + fi + + cp $binary_path $rootfs/$binary_path + if [ $? -ne 0 ]; then + echo "Failed to copy $binary_path to rootfs" + return 1 + fi + + return 0 +} + +install_busybox() +{ + rootfs=$1 + name=$2 + res=0 + tree="\ +$rootfs/selinux \ +$rootfs/dev \ +$rootfs/home \ +$rootfs/root \ +$rootfs/etc \ +$rootfs/etc/init.d \ +$rootfs/bin \ +$rootfs/usr/bin \ +$rootfs/sbin \ +$rootfs/usr/sbin \ +$rootfs/proc \ +$rootfs/sys \ +$rootfs/mnt \ +$rootfs/tmp \ +$rootfs/var/log \ +$rootfs/usr/share/udhcpc \ +$rootfs/dev/pts \ +$rootfs/dev/shm \ +$rootfs/lib \ +$rootfs/usr/lib \ +$rootfs/lib64 \ +$rootfs/usr/lib64" + + mkdir -p $tree || return 1 + chmod 755 $tree || return 1 + + pushd $rootfs/dev > /dev/null || return 1 + + # minimal devices needed for busybox + if [ $in_userns -eq 1 ]; then + for dev in tty console tty0 tty1 ram0 null urandom; do + echo "lxc.mount.entry = /dev/$dev dev/$dev none bind,optional,create=file 0 0" >> $path/config + done + else + mknod -m 666 tty c 5 0 || res=1 + mknod -m 666 console c 5 1 || res=1 + mknod -m 666 tty0 c 4 0 || res=1 + mknod -m 666 tty1 c 4 0 || res=1 + mknod -m 666 tty5 c 4 0 || res=1 + mknod -m 600 ram0 b 1 0 || res=1 + mknod -m 666 null c 1 3 || res=1 + mknod -m 666 zero c 1 5 || res=1 + mknod -m 666 urandom c 1 9 || res=1 + fi + + popd > /dev/null + + # root user defined + cat <> $rootfs/etc/passwd +root:x:0:0:root:/root:/bin/sh +EOF + + cat <> $rootfs/etc/group +root:x:0:root +EOF + + # mount everything + cat <> $rootfs/etc/init.d/rcS +#!/bin/sh +/bin/syslogd +/bin/mount -a +/bin/udhcpc +EOF + + # executable + chmod 744 $rootfs/etc/init.d/rcS || return 1 + + # launch rcS first then make a console available + # and propose a shell on the tty, the last one is + # not needed + cat <> $rootfs/etc/inittab +::sysinit:/etc/init.d/rcS +tty1::respawn:/bin/getty -L tty1 115200 vt100 +console::askfirst:/bin/sh +EOF + # writable and readable for other + chmod 644 $rootfs/etc/inittab || return 1 + + cat <> $rootfs/usr/share/udhcpc/default.script +#!/bin/sh +case "\$1" in + deconfig) + ip addr flush dev \$interface + ;; + + renew|bound) + # flush all the routes + if [ -n "\$router" ]; then + ip route del default 2> /dev/null + fi + + # check broadcast + if [ -n "\$broadcast" ]; then + broadcast="broadcast \$broadcast" + fi + + # add a new ip address + ip addr add \$ip/\$mask \$broadcast dev \$interface + + if [ -n "\$router" ]; then + ip route add default via \$router dev \$interface + fi + + [ -n "\$domain" ] && echo search \$domain > /etc/resolv.conf + for i in \$dns ; do + echo nameserver \$i >> /etc/resolv.conf + done + ;; +esac +exit 0 +EOF + + chmod 744 $rootfs/usr/share/udhcpc/default.script + + return $res +} + +install_dropbear() +{ + # copy dropbear binary + copy_binary dropbear || return 1 + + # make symlinks to various ssh utilities + utils="\ + $rootfs/usr/bin/dbclient \ + $rootfs/usr/bin/scp \ + $rootfs/usr/bin/ssh \ + $rootfs/usr/sbin/dropbearkey \ + $rootfs/usr/sbin/dropbearconvert \ + " + echo $utils | xargs -n1 ln -s /usr/sbin/dropbear + + # add necessary config files + mkdir $rootfs/etc/dropbear + dropbearkey -t rsa -f $rootfs/etc/dropbear/dropbear_rsa_host_key > /dev/null 2>&1 + dropbearkey -t dss -f $rootfs/etc/dropbear/dropbear_dss_host_key > /dev/null 2>&1 + + echo "'dropbear' ssh utility installed" + + return 0 +} + +install_openssh() +{ + # tools to be installed + server_utils="sshd" + client_utils="\ + ssh \ + scp \ + " + client_optional_utils="\ + sftp \ + ssh-add \ + ssh-agent \ + ssh-keygen \ + ssh-keyscan \ + ssh-argv0 \ + ssh-copy-id \ + " + + # new folders used by ssh + ssh_tree="\ +$rootfs/etc/ssh \ +$rootfs/var/empty/sshd \ +$rootfs/var/lib/empty/sshd \ +$rootfs/var/run/sshd \ +" + + # create folder structure + mkdir -p $ssh_tree + if [ $? -ne 0 ]; then + return 1 + fi + + # copy binaries + for bin in $server_utils $client_utils; do + copy_binary $bin || return 1 + done + + for bin in $client_optional_utils; do + tool_path=`which $bin` && copy_binary $bin + done + + # add user and group + cat <> $rootfs/etc/passwd +sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin +EOF + + cat <> $rootfs/etc/group +sshd:x:74: +EOF + + # generate container keys + ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key >/dev/null 2>&1 + ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key >/dev/null 2>&1 + + # by default setup root password with no password + cat < $rootfs/etc/ssh/sshd_config +Port 22 +Protocol 2 +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_dsa_key +UsePrivilegeSeparation yes +KeyRegenerationInterval 3600 +ServerKeyBits 768 +SyslogFacility AUTH +LogLevel INFO +LoginGraceTime 120 +PermitRootLogin yes +StrictModes yes +RSAAuthentication yes +PubkeyAuthentication yes +IgnoreRhosts yes +RhostsRSAAuthentication no +HostbasedAuthentication no +PermitEmptyPasswords yes +ChallengeResponseAuthentication no +EOF + + echo "'OpenSSH' utility installed" + + return 0 +} + +configure_busybox() +{ + rootfs=$1 + + which busybox >/dev/null 2>&1 + + if [ $? -ne 0 ]; then + echo "busybox executable is not accessible" + return 1 + fi + + # copy busybox in the rootfs + cp $(which busybox) $rootfs/bin + if [ $? -ne 0 ]; then + echo "failed to copy busybox in the rootfs" + return 1 + fi + + # symlink busybox for the commands it supports + # it would be nice to just use "chroot $rootfs busybox --install -s /bin" + # but that only works right in a chroot with busybox >= 1.19.0 + pushd $rootfs/bin > /dev/null || return 1 + ./busybox --help | grep 'Currently defined functions:' -A300 | \ + grep -v 'Currently defined functions:' | tr , '\n' | \ + xargs -n1 ln -s busybox + popd > /dev/null + + # relink /sbin/init + ln $rootfs/bin/busybox $rootfs/sbin/init + + # /etc/fstab must exist for "mount -a" + touch $rootfs/etc/fstab + + # passwd exec must be setuid + chmod +s $rootfs/bin/passwd + touch $rootfs/etc/shadow + + return 0 +} + +copy_configuration() +{ + path=$1 + rootfs=$2 + name=$3 + +grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config +cat <> $path/config +lxc.signal.halt = SIGUSR1 +lxc.signal.reboot = SIGTERM +lxc.uts.name = $name +lxc.tty.max = 1 +lxc.pty.max = 1 +lxc.cap.drop = sys_module mac_admin mac_override sys_time + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed +lxc.mount.entry = shm /dev/shm tmpfs defaults 0 0 +EOF + + libdirs="\ + lib \ + usr/lib \ + lib64 \ + usr/lib64" + + for dir in $libdirs; do + if [ -d "/$dir" ] && [ -d "$rootfs/$dir" ]; then + echo "lxc.mount.entry = /$dir $dir none ro,bind 0 0" >> $path/config + fi + done + echo "lxc.mount.entry = /sys/kernel/security sys/kernel/security none ro,bind,optional 0 0" >>$path/config +} + +remap_userns() +{ + path=$1 + + if [ -n "$LXC_MAPPED_UID" ] && [ "$LXC_MAPPED_UID" != "-1" ]; then + chown $LXC_MAPPED_UID $path/config >/dev/null 2>&1 + chown -R root $path/rootfs >/dev/null 2>&1 + fi + + if [ -n "$LXC_MAPPED_GID" ] && [ "$LXC_MAPPED_GID" != "-1" ]; then + chgrp $LXC_MAPPED_GID $path/config >/dev/null 2>&1 + chgrp -R root $path/rootfs >/dev/null 2>&1 + fi +} + +usage() +{ + cat < -s|--ssh={dropbear,openssh} +EOF + return 0 +} + +options=$(getopt -o hp:n:s: -l help,rootfs:,path:,name:,mapped-uid:,mapped-gid:,ssh: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; + -n|--name) name=$2; shift 2;; + --mapped-uid) LXC_MAPPED_UID=$2; shift 2;; + --mapped-gid) LXC_MAPPED_GID=$2; shift 2;; + -s|--ssh) SSH=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -z "$path" ]; then + echo "'path' parameter is required" + exit 1 +fi + +# detect rootfs +config="$path/config" +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) + else + rootfs=$path/rootfs + fi +fi + +install_busybox $rootfs $name +if [ $? -ne 0 ]; then + echo "failed to install busybox's rootfs" + exit 1 +fi + +configure_busybox $rootfs +if [ $? -ne 0 ]; then + echo "failed to configure busybox template" + exit 1 +fi + +copy_configuration $path $rootfs $name +if [ $? -ne 0 ]; then + echo "failed to write configuration file" + exit 1 +fi + +remap_userns $path +if [ $? -ne 0 ]; then + echo "failed to remap files to user" + exit 1 +fi + +if [ -n "$SSH" ]; then + case "$SSH" in + "dropbear") + install_dropbear + if [ $? -ne 0 ]; then + echo "Unable to install 'dropbear' ssh utility" + exit 1 + fi ;; + "openssh") + install_openssh + if [ $? -ne 0 ]; then + echo "Unable to install 'OpenSSH' utility" + exit 1 + fi ;; + *) + echo "$SSH: unrecognized ssh utility" + exit 1 + esac +else + which dropbear >/dev/null 2>&1 + if [ $? -eq 0 ]; then + install_dropbear + fi +fi diff --git a/templates/lxc-centos.in b/templates/lxc-centos.in new file mode 100644 index 0000000..eba5ec8 --- /dev/null +++ b/templates/lxc-centos.in @@ -0,0 +1,968 @@ +#!/bin/bash + +# +# template script for generating CentOS container for LXC + +# +# lxc: linux Container library + +# Authors: +# Daniel Lezcano +# Ramez Hanna +# Fajar A. Nugraha +# Michael H. Warfield + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#Configurations +default_path=@LXCPATH@ + +# Some combinations of the tuning knobs below do not exactly make sense. +# but that's ok. +# +# If the "root_password" is non-blank, use it, else set a default. +# This can be passed to the script as an environment variable and is +# set by a shell conditional assignment. Looks weird but it is what it is. +# +# If the root password contains a ding ($) then try to expand it. +# That will pick up things like ${name} and ${RANDOM}. +# If the root password contains more than 3 consecutive X's, pass it as +# a template to mktemp and take the result. +# +# If root_display_password = yes, display the temporary root password at exit. +# If root_store_password = yes, store it in the configuration directory +# If root_prompt_password = yes, invoke "passwd" to force the user to change +# the root password after the container is created. +# If root_expire_password = yes, you will be prompted to change the root +# password at the first login. +# +# These are conditional assignments... The can be overridden from the +# preexisting environment variables... +# +# Make sure this is in single quotes to defer expansion to later! +# :{root_password='Root-${name}-${RANDOM}'} +: ${root_password='Root-${name}-XXXXXX'} + +# Now, it doesn't make much sense to display, store, and force change +# together. But, we gotta test, right??? +: ${root_display_password='no'} +: ${root_store_password='yes'} +# Prompting for something interactive has potential for mayhem +# with users running under the API... Don't default to "yes" +: ${root_prompt_password='no'} + +# Expire root password? Default to yes, but can be overridden from +# the environment variable +: ${root_expire_password='yes'} + +# These are only going into comments in the resulting config... +lxc_network_type=veth +lxc_network_link=lxcbr0 + +# is this CentOS? +# Alow for weird remixes like the Raspberry Pi +# +# Use the Mitre standard CPE identifier for the release ID if possible... +# This may be in /etc/os-release or /etc/system-release-cpe. We +# should be able to use EITHER. Give preference to /etc/os-release for now. + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +if [ -e /etc/os-release ] +then +# This is a shell friendly configuration file. We can just source it. +# What we're looking for in here is the ID, VERSION_ID and the CPE_NAME + . /etc/os-release + echo "Host CPE ID from /etc/os-release: ${CPE_NAME}" +fi + +if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ] +then + CPE_NAME=$(head -n1 /etc/system-release-cpe) + CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)') + if [ "${CPE_URI}" != "cpe:/o" ] + then + CPE_NAME= + else + # Probably a better way to do this but sill remain posix + # compatible but this works, shrug... + # Must be nice and not introduce convenient bashisms here. + # + # According to the official registration at Mitre and NIST, + # this should have been something like this for CentOS: + # cpe:/o:centos:centos:6 + # or this: + # cpe:/o:centos:centos:6.5 + # + ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)') + # The "enterprise_linux" is a bone toss back to RHEL. + # Since CentOS and RHEL are so tightly coupled, we'll + # take the RHEL version if we're running on it and do the + # equivalent version for CentOS. + if [ ${ID} = "linux" -o ${ID} = "enterprise_linux" ] + then + # Instead we got this: cpe:/o:centos:linux:6 + ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:\([^:]*\)') + fi + + VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)') + echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}" + fi +fi + +if [ "${CPE_NAME}" != "" -a "${ID}" = "centos" -a "${VERSION_ID}" != "" ] +then + centos_host_ver=${VERSION_ID} + is_centos=true +elif [ "${CPE_NAME}" != "" -a "${ID}" = "redhat" -o "${ID}" = "rhel" -a "${VERSION_ID}" != "" ] +then + # RHEL 7+ /etc/os-release ID = 'rhel', which doesn't enter this elif without the added OR statement + redhat_host_ver=${VERSION_ID} + is_redhat=true +elif [ -e /etc/centos-release ] +then + # Only if all other methods fail, try to parse the redhat-release file. + centos_host_ver=$( sed -e '/^CentOS /!d' -e 's/CentOS.*\srelease\s*\([0-9][0-9.]*\)\s.*/\1/' < /etc/centos-release ) + if [ "$centos_host_ver" != "" ] + then + is_centos=true + fi +fi + +force_mknod() +{ + # delete a device node if exists, and create a new one + rm -f $2 && mknod -m $1 $2 $3 $4 $5 +} + +configure_centos() +{ + + # disable selinux in CentOS + mkdir -p $rootfs_path/selinux + echo 0 > $rootfs_path/selinux/enforce + + # Also kill it in the /etc/selinux/config file if it's there... + if [ -f $rootfs_path/etc/selinux/config ] + then + sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config + fi + + # Nice catch from Dwight Engen in the Oracle template. + # Wantonly plagerized here with much appreciation. + if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then + mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig + ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled + fi + + # This is a known problem and documented in RedHat bugzilla as relating + # to a problem with auditing enabled. This prevents an error in + # the container "Cannot make/remove an entry for the specified session" + sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login + sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd + + if [ -f ${rootfs_path}/etc/pam.d/crond ] + then + sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond + fi + + # In addition to disabling pam_loginuid in the above config files + # we'll also disable it by linking it to pam_permit to catch any + # we missed or any that get installed after the container is built. + # + # Catch either or both 32 and 64 bit archs. + if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ] + then + ( cd ${rootfs_path}/lib/security/ + mv pam_loginuid.so pam_loginuid.so.disabled + ln -s pam_permit.so pam_loginuid.so + ) + fi + + if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ] + then + ( cd ${rootfs_path}/lib64/security/ + mv pam_loginuid.so pam_loginuid.so.disabled + ln -s pam_permit.so pam_loginuid.so + ) + fi + + # Set default localtime to the host localtime if not set... + if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ] + then + # if /etc/localtime is a symlink, this should preserve it. + cp -a /etc/localtime ${rootfs_path}/etc/localtime + fi + + # Deal with some dain bramage in the /etc/init.d/halt script. + # Trim it and make it our own and link it in before the default + # halt script so we can intercept it. This also preventions package + # updates from interferring with our interferring with it. + # + # There's generally not much in the halt script that useful but what's + # in there from resetting the hardware clock down is generally very bad. + # So we just eliminate the whole bottom half of that script in making + # ourselves a copy. That way a major update to the init scripts won't + # trash what we've set up. + if [ -f ${rootfs_path}/etc/init.d/halt ] + then + sed -e '/hwclock/,$d' \ + < ${rootfs_path}/etc/init.d/halt \ + > ${rootfs_path}/etc/init.d/lxc-halt + + echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt + chmod 755 ${rootfs_path}/etc/init.d/lxc-halt + + # Link them into the rc directories... + ( + cd ${rootfs_path}/etc/rc.d/rc0.d + ln -s ../init.d/lxc-halt S00lxc-halt + cd ${rootfs_path}/etc/rc.d/rc6.d + ln -s ../init.d/lxc-halt S00lxc-reboot + ) + fi + + # configure the network using the dhcp + cat < ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0 +DEVICE=eth0 +BOOTPROTO=dhcp +ONBOOT=yes +HOSTNAME=${utsname} +NM_CONTROLLED=no +TYPE=Ethernet +MTU=${MTU} +DHCP_HOSTNAME=\`hostname\` +EOF + + # set the hostname + cat < ${rootfs_path}/etc/sysconfig/network +NETWORKING=yes +HOSTNAME=${utsname} +EOF + + # set minimal hosts + cat < $rootfs_path/etc/hosts +127.0.0.1 localhost $name +EOF + + # set minimal fstab + cat < $rootfs_path/etc/fstab +/dev/root / rootfs defaults 0 0 +EOF + + # create lxc compatibility init script + if [ "$release" = "6" ]; then + cat < $rootfs_path/etc/init/lxc-sysinit.conf +start on startup +env container + +pre-start script + if [ "x\$container" != "xlxc" -a "x\$container" != "xlibvirt" ]; then + stop; + fi + + rm -f /var/lock/subsys/* + rm -f /var/run/*.pid + [ -e /etc/mtab ] || ln -s /proc/mounts /etc/mtab + mkdir -p /dev/shm + mount -t tmpfs -o nosuid,nodev tmpfs /dev/shm + + initctl start tty TTY=console + telinit 3 + exit 0 +end script +EOF + elif [ "$release" = "5" ]; then + cat < $rootfs_path/etc/rc.d/lxc.sysinit +#! /bin/bash +rm -f /etc/mtab /var/run/*.{pid,lock} /var/lock/subsys/* +rm -rf {/,/var}/tmp/* +echo "/dev/root / rootfs defaults 0 0" > /etc/mtab +exit 0 +EOF + chmod 755 $rootfs_path/etc/rc.d/lxc.sysinit + sed -i 's|si::sysinit:/etc/rc.d/rc.sysinit|si::bootwait:/etc/rc.d/lxc.sysinit|' $rootfs_path/etc/inittab + # prevent mingetty from calling vhangup(2) since it fails with userns. + # Same issue as oracle template: prevent mingetty from calling vhangup(2) + # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589. + sed -i 's|^1:|co:2345:respawn:/sbin/mingetty --nohangup console\n1:|' $rootfs_path/etc/inittab + sed -i 's|^\([56]:\)|#\1|' $rootfs_path/etc/inittab + fi + + dev_path="${rootfs_path}/dev" + rm -rf $dev_path + mkdir -p $dev_path + mknod -m 666 ${dev_path}/null c 1 3 + mknod -m 666 ${dev_path}/zero c 1 5 + mknod -m 666 ${dev_path}/random c 1 8 + mknod -m 666 ${dev_path}/urandom c 1 9 + mkdir -m 755 ${dev_path}/pts + mkdir -m 1777 ${dev_path}/shm + mknod -m 666 ${dev_path}/tty c 5 0 + mknod -m 666 ${dev_path}/tty0 c 4 0 + mknod -m 666 ${dev_path}/tty1 c 4 1 + mknod -m 666 ${dev_path}/tty2 c 4 2 + mknod -m 666 ${dev_path}/tty3 c 4 3 + mknod -m 666 ${dev_path}/tty4 c 4 4 + mknod -m 600 ${dev_path}/console c 5 1 + mknod -m 666 ${dev_path}/full c 1 7 + mknod -m 600 ${dev_path}/initctl p + mknod -m 666 ${dev_path}/ptmx c 5 2 + + # setup console and tty[1-4] for login. note that /dev/console and + # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and + # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. + # lxc will maintain these links and bind mount ptys over /dev/lxc/* + # since lxc.tty.dir is specified in the config. + + # allow root login on console, tty[1-4], and pts/0 for libvirt + echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty + echo "lxc/console" >>${rootfs_path}/etc/securetty + echo "lxc/tty1" >>${rootfs_path}/etc/securetty + echo "lxc/tty2" >>${rootfs_path}/etc/securetty + echo "lxc/tty3" >>${rootfs_path}/etc/securetty + echo "lxc/tty4" >>${rootfs_path}/etc/securetty + echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty + echo "pts/0" >>${rootfs_path}/etc/securetty + + # prevent mingetty from calling vhangup(2) since it fails with userns. + # Same issue as oracle template: prevent mingetty from calling vhangup(2) + # commit 2e83f7201c5d402478b9849f0a85c62d5b9f1589. + test -f $rootfs_path/etc/init/tty.conf && sed -i 's|mingetty|mingetty --nohangup|' $rootfs_path/etc/init/tty.conf + + if [ ${root_display_password} = "yes" ] + then + echo "Setting root password to '$root_password'" + fi + if [ ${root_store_password} = "yes" ] + then + touch ${config_path}/tmp_root_pass + chmod 600 ${config_path}/tmp_root_pass + echo ${root_password} > ${config_path}/tmp_root_pass + echo "Storing root password in '${config_path}/tmp_root_pass'" + fi + + echo "root:$root_password" | chroot $rootfs_path chpasswd + + if [ ${root_expire_password} = "yes" ] + then + # Also set this password as expired to force the user to change it! + chroot $rootfs_path passwd -e root + fi + + # This will need to be enhanced for CentOS 7 when systemd + # comes into play... /\/\|=mhw=|\/\/ + + return 0 +} + +configure_centos_init() +{ + test -f ${rootfs_path}/etc/rc.sysinit && sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit + test -f ${rootfs_path}/etc/rc.d/rc.sysinit && sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit + if [ "$release" = "6" ]; then + chroot ${rootfs_path} chkconfig udev-post off + fi + chroot ${rootfs_path} chkconfig network on + + if [ "$release" = "7" ]; then + # don't wait for the timeout + chroot ${rootfs_path} chkconfig systemd-remount-fs off + fi + + if [ -d ${rootfs_path}/etc/init ] + then + # This is to make upstart honor SIGPWR + cat <${rootfs_path}/etc/init/power-status-changed.conf +# power-status-changed - shutdown on SIGPWR +# +start on power-status-changed + +exec /sbin/shutdown -h now "SIGPWR received" +EOF + fi +} + +download_centos() +{ + + # check the mini CentOS was not already downloaded + INSTALL_ROOT=$cache/partial + mkdir -p $INSTALL_ROOT + if [ $? -ne 0 ]; then + echo "Failed to create '$INSTALL_ROOT' directory" + return 1 + fi + + # download a mini CentOS into a cache + echo "Downloading CentOS minimal ..." + YUM0="yum --installroot $INSTALL_ROOT -y --nogpgcheck" + + if yum -h | grep -q 'releasever=RELEASEVER'; then + YUM="$YUM0 --releasever=$release" + else + YUM="$YUM0" + fi + PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils cronie" + + # use temporary repository definition + # always prefer the repo given by the user + # if no repo given, use mirrorlist.centos.org for i386 and x86_64 + # and http://mirror.centos.org/altarch/ otherwise + REPO_FILE=$INSTALL_ROOT/etc/yum.repos.d/lxc-centos-temp.repo + mkdir -p $(dirname $REPO_FILE) + if [ -n "$repo" ]; then + cat < $REPO_FILE +[base] +name=local repository +baseurl="$repo" +EOF + elif [ ${basearch} = 'i386' ] || [ ${basearch} = 'x86_64' ]; then + cat < $REPO_FILE +[base] +name=CentOS-$release - Base +mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=os + +[updates] +name=CentOS-$release - Updates +mirrorlist=http://mirrorlist.centos.org/?release=$release&arch=$basearch&repo=updates +EOF + else + cat < $REPO_FILE +[base] +name=CentOS-$release - Base +baseurl=http://mirror.centos.org/altarch/7/os/$basearch/ + +[updates] +name=CentOS-$release - Updates +baseurl=http://mirror.centos.org/altarch/7/updates/$basearch/ +EOF + fi + + # create minimal device nodes, needed for "yum install" and "yum update" process + mkdir -p $INSTALL_ROOT/dev + force_mknod 666 $INSTALL_ROOT/dev/null c 1 3 + force_mknod 666 $INSTALL_ROOT/dev/urandom c 1 9 + + # create /run directory, just in case it is missing (e.g. RHEL7) + mkdir -p $INSTALL_ROOT/run + + $YUM install $PKG_LIST + + # create symlink for /var/run -> ../run + if [ "$release" = "7" ]; then + mv $INSTALL_ROOT/var/run/* $INSTALL_ROOT/run/ + rmdir $INSTALL_ROOT/var/run + ln -sf ../run $INSTALL_ROOT/var/run + fi + + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + + # use same nameservers as hosts, needed for "yum update later" + cp /etc/resolv.conf $INSTALL_ROOT/etc/ + + # check whether rpmdb is under $HOME + if [ ! -e $INSTALL_ROOT/var/lib/rpm/Packages -a -e $INSTALL_ROOT/$HOME/.rpmdb/Packages ]; then + echo "Fixing rpmdb location ..." + mv $INSTALL_ROOT/$HOME/.rpmdb/[A-Z]* $INSTALL_ROOT/var/lib/rpm/ + rm -rf $INSTALL_ROOT/$HOME/.rpmdb + chroot $INSTALL_ROOT rpm --rebuilddb 2>/dev/null + fi + + # check whether rpmdb version is correct + chroot $INSTALL_ROOT rpm --quiet -q yum 2>/dev/null + ret=$? + + # if "rpm -q" doesn't work due to rpmdb version difference, + # then we need to redo the process using the newly-installed yum + if [ $ret -gt 0 ]; then + echo "Reinstalling packages ..." + mv $REPO_FILE $REPO_FILE.tmp + mkdir $INSTALL_ROOT/etc/yum.repos.disabled + mv $INSTALL_ROOT/etc/yum.repos.d/*.repo $INSTALL_ROOT/etc/yum.repos.disabled/ + mv $REPO_FILE.tmp $REPO_FILE + mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/etc + cp /etc/resolv.conf $INSTALL_ROOT/$INSTALL_ROOT/etc/ + mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/dev + mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/null c 1 3 + mknod -m 666 $INSTALL_ROOT/$INSTALL_ROOT/dev/urandom c 1 9 + mkdir -p $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum + cp -al $INSTALL_ROOT/var/cache/yum/* $INSTALL_ROOT/$INSTALL_ROOT/var/cache/yum/ + chroot $INSTALL_ROOT $YUM0 install $PKG_LIST + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + mv $INSTALL_ROOT/$INSTALL_ROOT $INSTALL_ROOT.tmp + rm -rf $INSTALL_ROOT + mv $INSTALL_ROOT.tmp $INSTALL_ROOT + fi + + rm -f $REPO_FILE + rm -rf $INSTALL_ROOT/var/cache/yum/* + + mv "$INSTALL_ROOT" "$cache/rootfs" + echo "Download complete." + + return 0 +} + +copy_centos() +{ + + # make a local copy of the mini CentOS + echo -n "Copying rootfs to $rootfs_path ..." + #cp -a $cache/rootfs-$arch $rootfs_path || return 1 + # i prefer rsync (no reason really) + mkdir -p $rootfs_path + rsync -SHaAX $cache/rootfs/ $rootfs_path/ + echo + return 0 +} + +update_centos() +{ + YUM="chroot $cache/rootfs yum -y --nogpgcheck" + $YUM update + if [ $? -ne 0 ]; then + return 1 + fi + $YUM clean packages +} + +install_centos() +{ + mkdir -p /var/lock/subsys/ + ( + flock -x 9 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + echo "Checking cache download in $cache/rootfs ... " + if [ ! -e "$cache/rootfs" ]; then + download_centos + if [ $? -ne 0 ]; then + echo "Failed to download 'CentOS base'" + return 1 + fi + else + echo "Cache found. Updating..." + update_centos + if [ $? -ne 0 ]; then + echo "Failed to update 'CentOS base', continuing with last known good cache" + else + echo "Update finished" + fi + fi + + echo "Copy $cache/rootfs to $rootfs_path ... " + copy_centos + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + + return 0 + + ) 9>/var/lock/subsys/lxc-centos + + return $? +} + +create_hwaddr() +{ + openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' +} + +copy_configuration() +{ + mkdir -p $config_path + + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo " +lxc.rootfs.path = $rootfs_path +" >> $config_path/config + + # The following code is to create static MAC addresses for each + # interface in the container. This code will work for multiple + # interfaces in the default config. + mv $config_path/config $config_path/config.def + while read LINE + do + # This should catch variable expansions from the default config... + if expr "${LINE}" : '.*\$' > /dev/null 2>&1 + then + LINE=$(eval "echo \"${LINE}\"") + fi + + # There is a tab and a space in the regex bracket below! + # Seems that \s doesn't work in brackets. + KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') + + if [[ "${KEY}" != "lxc.net.0.hwaddr" ]] + then + echo ${LINE} >> $config_path/config + + if [[ "${KEY}" == "lxc.net.0.link" ]] + then + echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $config_path/config + fi + fi + done < $config_path/config.def + + rm -f $config_path/config.def + + if [ -e "@LXCTEMPLATECONFIG@/centos.common.conf" ]; then + echo " +# Include common configuration +lxc.include = @LXCTEMPLATECONFIG@/centos.common.conf +" >> $config_path/config + fi + + # Append things which require expansion here... + cat <> $config_path/config +lxc.arch = $arch +lxc.uts.name = $utsname + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +# example simple networking setup, uncomment to enable +#lxc.net.0.type = $lxc_network_type +#lxc.net.0.flags = up +#lxc.net.0.link = $lxc_network_link +#lxc.net.0.name = eth0 +# Additional example for veth network type +# static MAC address, +#lxc.net.0.hwaddr = 00:16:3e:77:52:20 +# persistent veth device name on host side +# Note: This may potentially collide with other containers of same name! +#lxc.net.0.veth.pair = v-$name-e0 + +EOF + + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +clean() +{ + + if [ ! -e $cache ]; then + exit 0 + fi + + # lock, so we won't purge while someone is creating a repository + ( + flock -x 9 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + + echo -n "Purging the download cache for CentOS-$release..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 + + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-centos +} + +usage() +{ + cat < + [-p|--path=] [-c|--clean] [-R|--release=] [-a|--arch=] + [-h|--help] +Mandatory args: + -n,--name container name, used to as an identifier for that container from now on +Optional args: + -p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc/name. + -c,--clean clean the cache + -R,--release CentOS release for the new container. If the host is CentOS, then it will default to the host's release. + --fqdn fully qualified domain name (FQDN) for DNS and system naming + --repo repository to use (url) + -a,--arch Define what arch the container will be [i686,x86_64] + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,repo:,arch:,fqdn: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi + +arch=$(uname -m) +eval set -- "$options" +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs_path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -c|--clean) clean=1; shift 1;; + -R|--release) release=$2; shift 2;; + --repo) repo="$2"; shift 2;; + -a|--arch) newarch=$2; shift 2;; + --fqdn) utsname=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ ! -z "$clean" -a -z "$path" ]; then + clean || exit 1 + exit 0 +fi + +basearch=${arch} +# Map a few architectures to their generic CentOS repository archs. +# The two ARM archs are a bit of a guesstimate for the v5 and v6 +# archs. V6 should have hardware floating point (Rasberry Pi). +# The "arm" arch is safer (no hardware floating point). So +# there may be cases where we "get it wrong" for some v6 other +# than RPi. +case "$arch" in +i686) basearch=i386 ;; +armv3l|armv4l|armv5l) basearch=arm ;; +armv6l|armv7l|armv8l) basearch=armhfp ;; +*) ;; +esac + +# Somebody wants to specify an arch. This is very limited case. +# i386/i586/i686 on i386/x86_64 +# - or - +# x86_64 on x86_64 +if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ] +then + case "${newarch}" in + i386|i586|i686) + if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ] + then + # Make the arch a generic x86 32 bit... + arch=${newarch} + basearch=i386 + else + basearch=bad + fi + ;; + *) + basearch=bad + ;; + esac + + if [ "${basearch}" = "bad" ] + then + echo "You cannot build a ${newarch} CentOS container on a ${arch} host. Sorry!" + exit 1 + fi +fi + +# Allow the cache base to be set by environment variable +cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/centos/$basearch + +# Let's do something better for the initial root password. +# It's not perfect but it will defeat common scanning brute force +# attacks in the case where ssh is exposed. It will also be set to +# expired, forcing the user to change it at first login. +if [ "${root_password}" = "" ] +then + root_password=Root-${name}-${RANDOM} +else + # If it's got a ding in it, try and expand it! + if [ $(expr "${root_password}" : '.*$.') != 0 ] + then + root_password=$(eval echo "${root_password}") + fi + + # If it has more than 3 consecutive X's in it, feed it + # through mktemp as a template. + if [ $(expr "${root_password}" : '.*XXXX') != 0 ] + then + root_password=$(mktemp -u ${root_password}) + fi +fi + +if [ -z "${utsname}" ]; then + utsname=${name} +fi + +# This follows a standard "resolver" convention that an FQDN must have +# at least two dots or it is considered a local relative host name. +# If it doesn't, append the dns domain name of the host system. +# +# This changes one significant behavior when running +# "lxc_create -n Container_Name" without using the +# --fqdn option. +# +# Old behavior: +# utsname and hostname = Container_Name +# New behavior: +# utsname and hostname = Container_Name.Domain_Name + +if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then + if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then + utsname=${utsname}.$(dnsdomainname) + fi +fi + +type yum >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "'yum' command is missing" + exit 1 +fi + +if [ -z "$path" ]; then + path=$default_path/$name +fi + +if [ -z "$release" ]; then + if [ "$is_centos" -a "$centos_host_ver" ]; then + release=$centos_host_ver + elif [ "$is_redhat" -a "$redhat_host_ver" ]; then + # This is needed to clean out bullshit like 6workstation and 6server. + release=$(expr $redhat_host_ver : '\([0-9.]*\)') + else + echo "This is not a CentOS or Red Hat host and release is missing, defaulting to 7, use -R|--release to specify release" + release=7 + fi +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -z "$rootfs_path" ]; then + rootfs_path=$path/rootfs + # check for 'lxc.rootfs.path' passed in through default config by lxc-create + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(sed -e '/^lxc.rootfs.path\s*=/!d' -e 's/\s*#.*//' \ + -e 's/^lxc.rootfs.path\s*=\s*//' -e q $path/config) + fi +fi +config_path=$path +cache=$cache_base/$release + +revert() +{ + echo "Interrupted, so cleaning up" + lxc-destroy -n $name + # maybe was interrupted before copy config + rm -rf $path + echo "exiting..." + exit 1 +} + +trap revert SIGHUP SIGINT SIGTERM + +copy_configuration +if [ $? -ne 0 ]; then + echo "failed write configuration file" + exit 1 +fi + +install_centos +if [ $? -ne 0 ]; then + echo "failed to install CentOS" + exit 1 +fi + +configure_centos +if [ $? -ne 0 ]; then + echo "failed to configure CentOS for a container" + exit 1 +fi + +configure_centos_init + +if [ ! -z "$clean" ]; then + clean || exit 1 + exit 0 +fi +echo " +Container rootfs and config have been created. +Edit the config file to check/enable networking setup. +" + +if [ ${root_display_password} = "yes" ] +then + echo "The temporary password for root is: '$root_password' + +You may want to note that password down before starting the container. +" +fi + +if [ ${root_store_password} = "yes" ] +then + echo "The temporary root password is stored in: + + '${config_path}/tmp_root_pass' +" +fi + +if [ ${root_prompt_password} = "yes" ] +then + echo "Invoking the passwd command in the container to set the root password. + + chroot ${rootfs_path} passwd +" + chroot ${rootfs_path} passwd +else + if [ ${root_expire_password} = "yes" ] + then + if ( mountpoint -q -- "${rootfs_path}" ) + then + echo "To reset the root password, you can do: + + lxc-start -n ${name} + lxc-attach -n ${name} -- passwd + lxc-stop -n ${name} +" + else + echo " +The root password is set up as "expired" and will require it to be changed +at first login, which you should do as soon as possible. If you lose the +root password or wish to change it without starting the container, you +can change it from the host by running the following command (which will +also reset the expired flag): + + chroot ${rootfs_path} passwd +" + fi + fi +fi diff --git a/templates/lxc-cirros.in b/templates/lxc-cirros.in new file mode 100644 index 0000000..de79daf --- /dev/null +++ b/templates/lxc-cirros.in @@ -0,0 +1,343 @@ +#!/bin/bash + +# template script for generating ubuntu container for LXC +# +# This script consolidates and extends the existing lxc ubuntu scripts +# + +# Copyright © 2013 Canonical Ltd. +# Author: Scott Moser +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2, as +# published by the Free Software Foundation. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# Detect use under userns (unsupported) +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +VERBOSITY=0 +DOWNLOAD_URL="http://download.cirros-cloud.net/" + +UNAME_M=$(uname -m) +ARCHES=( i386 x86_64 amd64 arm ) +STREAMS=( released devel ) +SOURCES=( nocloud none ) +BUILD="standard" +LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" + +LXC_MAPPED_GID= +LXC_MAPPED_UID= + +DEF_VERSION="released" +DEF_SOURCE="nocloud" +case "${UNAME_M}" in + i?86) DEF_ARCH="i386";; + x86_64) DEF_ARCH="x86_64";; + arm*) DEF_ARCH="arm";; + *) DEF_ARCH="i386";; +esac + +am_in_userns() { + [ -e /proc/self/uid_map ] || { echo no; return; } + [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; } + line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map) + [ "$line" = "0 0 4294967295" ] && { echo no; return; } + echo yes +} + +in_userns=0 +[ $(am_in_userns) = "yes" ] && in_userns=1 + +# Allow the cache base to be set by environment variable +if [ $(id -u) -eq 0 ]; then + CACHE_D=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc/cirros"} +else + CACHE_D=${LXC_CACHE_PATH:-"$HOME/.cache/lxc/cirros"} +fi + +error() { echo "$@" 1>&2; } +inargs() { + local needle="$1" x="" + shift + for x in "$@"; do + [ "$needle" = "$x" ] && return 0 + done + return 1 +} + +Usage() { + cat <&2; [ $# -eq 0 ] || error "$@"; return 1; } + +debug() { + local level=${1}; shift; + [ "${level}" -gt "${VERBOSITY}" ] && return + error "${@}" +} +jsondict() { + local k="" v="" ret="{" + for arg in "$@"; do + k="${arg%%=*}" + v="${arg#*=}" + ret="${ret} \"${k}\": \"$v\"," + done + ret="${ret%,} }" + echo "$ret" +} + +copy_configuration() +{ + local path=$1 rootfs=$2 name=$3 arch=$4 release=$5 +cat >> "$path/config" <> $path/config + fi + +} + +insert_ds_nocloud() { + local root_d="$1" authkey="$2" udfile="$3" + local sdir="$root_d/var/lib/cloud/seed/nocloud" + + mkdir -p "$sdir" || + { error "failed to make datasource dir $sdir"; return 1; } + rm -f "$sdir/meta-data" "$sdir/user-data" || + { error "failed to clean old data from $sdir"; return 1; } + + iid="iid-local01" + jsondict "instance-id=$iid" \ + ${authkeys:+"public-keys=${authkeys}"} > "$sdir/meta-data" || + { error "failed to write metadata to $sdir/meta-data"; return 1; } + + if [ -n "$udfile" ]; then + cat "$udfile" > "$sdir/user-data" || + { error "failed to write user-data to $sdir"; return 1; } + else + rm -f "$sdir/user-data" + fi +} + +insert_ds() { + local dstype="$1" root_d="$2" authkey="$3" udfile="$4" + case "$dstype" in + nocloud) insert_ds_nocloud "$root_d" "$authkey" "$udfile" + esac +} + +extract_rootfs() { + local tarball="$1" rootfs_d="$2" + mkdir -p "${rootfs_d}" || + { error "failed to make rootfs dir ${rootfs_d}"; return 1; } + + if [ $in_userns -eq 1 ]; then + tar -C "${rootfs_d}" --anchored --exclude="dev/*" -Sxzf "${tarball}" || + { error "failed to populate ${rootfs_d}"; return 1; } + else + tar -C "${rootfs_d}" -Sxzf "${tarball}" || + { error "failed to populate ${rootfs_d}"; return 1; } + fi + return 0 +} + +download_tarball() { + local arch="$1" ver="$2" cached="$3" baseurl="$4" + local out="" outd="" file="" dlpath="" + file="cirros-$ver-$arch-lxc.tar.gz" + dlpath="$ver/$file" + outd="${cached}/${dlpath%/*}" + if [ -f "$cached/$dlpath" ]; then + _RET="$cached/$dlpath" + return 0 + fi + + mkdir -p "${outd}" || + { error "failed to create ${outd}"; return 1; } + + debug 1 "downloading ${baseurl%/}/$dlpath" to "${cached}/$dlpath" + wget "${baseurl%/}/$dlpath" -O "$cached/${dlpath}.$$" && + mv "$cached/$dlpath.$$" "$cached/$dlpath" || { + rm -f "$cached/$dlpath.$$"; + error "failed to download $dlpath"; + return 1; + } + _RET="$cached/$dlpath" +} + +create_main() { + local short_opts="a:hn:p:S:uvV" + local long_opts="arch:,auth-key:,name:,path:,tarball:,userdata:,verbose,version:,rootfs:,mapped-uid:,mapped-gid:" + local getopt_out="" + getopt_out=$(getopt --name "${0##*/}" \ + --options "${short_opts}" --long "${long_opts}" -- "$@") && + eval set -- "${getopt_out}" || + { bad_Usage; return; } + + local arch="${DEF_ARCH}" dsource="${DEF_SOURCE}" version="${DEF_VERSION}" + local authkey_f="" authkeys="" userdata_f="" path="" tarball="" + local cur="" next="" + local rootfs_d="" + + while [ $# -ne 0 ]; do + cur=$1; next=$2; + case "$cur" in + -a|--arch) arch="$next"; shift;; + -h|--help) Usage ; return 0;; + -n|--name) name="$next"; shift;; + -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; + -S|--auth-key) authkey_f="$next"; shift;; + -p|--path) path=$next; shift;; + -v|--version) version=$next; shift;; + -u|--userdata) userdata_f="$next"; shift;; + --tarball) tarball="$next"; shift;; + --source) dsource="$next"; shift;; + --rootfs) rootfs_d="$next"; shift;; + --mapped-uid) LXC_MAPPED_UID=$next; shift;; + --mapped-gid) LXC_MAPPED_GID=$next; shift;; + --) shift; break;; + esac + shift; + done + + [ -n "$rootfs_d" ] || rootfs_d="$path/rootfs" + [ $# -eq 0 ] || { bad_Usage "unexpected arguments: $*"; return; } + [ -n "$path" ] || { error "'path' parameter is required"; return 1; } + + if [ "$(id -u)" != "0" ]; then + { error "must be run as root"; return 1; } + fi + + case "$arch" in + i?86) arch="i386";; + amd64) arch="x86_64";; + esac + + inargs "$arch" "${ARCHES[@]}" || + { error "bad arch '$arch'. allowed: ${ARCHES[*]}"; return 1; } + + inargs "$dsource" "${SOURCES[@]}" || + { error "bad source '$dsource'. allowed: ${SOURCES[*]}"; return 1; } + + if [ "$dsource" = "none" ] && [ -n "$userdata_f" -o -n "$authkey_f" ]; then + error "userdata and authkey are incompatible with --source=none"; + return 1; + fi + + if [ -n "$authkey_f" ]; then + if [ ! -f "$authkey_f" ]; then + error "--auth-key=${authkey_f} must reference a file" + return 1 + fi + authkeys=$(cat "$authkey_f") || + { error "failed to read ${authkey_f}"; return 1; } + fi + + if [ -n "$userdata_f" -a ! -f "${userdata_f}" ]; then + error "${userdata_f}: --userdata arg not a file" + return 1 + fi + + if [ -z "$tarball" ]; then + if inargs "$version" "${STREAMS[@]}"; then + out=$(wget -O - -q "${DOWNLOAD_URL%/}/version/$version") || + { error "failed to convert 'version=$version'"; return 1; } + version="$out" + fi + download_tarball "$arch" "$version" "${CACHE_D}" "${DOWNLOAD_URL}" || + return + tarball="$_RET" + fi + + extract_rootfs "${tarball}" "${rootfs_d}" || return + + if [ "$version" = "0.3.2~pre1" ]; then + debug 1 "fixing console for lxc and '$version'" + sed -i 's,^\(#console.* 115200 \)# /dev/console,\1 console,g' \ + "$rootfs_d/etc/inittab" || + { error "failed to fix console entry for $version"; return 1; } + fi + + if [ "$dsource" != "none" ]; then + insert_ds "$dsource" "$path/rootfs" "$authkeys" "$userdata_f" || { + error "failed to insert userdata to $path/rootfs" + return 1 + } + fi + + copy_configuration "$path" "$path/rootfs" "$name" "$arch" "$release" + return +} + +create_main "$@" + +# vi: ts=4 expandtab diff --git a/templates/lxc-debian.in b/templates/lxc-debian.in new file mode 100644 index 0000000..4388478 --- /dev/null +++ b/templates/lxc-debian.in @@ -0,0 +1,824 @@ +#!/bin/bash + +# +# lxc: linux Container library + +# Authors: +# Daniel Lezcano + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin +export GREP_OPTIONS="" + +MIRROR=${MIRROR:-http://deb.debian.org/debian} +SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.debian.org/} +LOCALSTATEDIR="@LOCALSTATEDIR@" +LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" +# Allows the lxc-cache directory to be set by environment variable +LXC_CACHE_PATH=${LXC_CACHE_PATH:-"$LOCALSTATEDIR/cache/lxc"} + +find_interpreter() +{ + given_interpreter=$(basename "$1") + + if [ ! -d /proc/sys/fs/binfmt_misc/ ] ; then + return 1 + fi + for file in /proc/sys/fs/binfmt_misc/* ; do + if [ "$file" = "/proc/sys/fs/binfmt_misc/register" -o \ + "$file" = "/proc/sys/fs/binfmt_misc/status" ] ; then + continue + fi + interpreter_path=$(sed -n "/^interpreter/s/interpreter \([^[:space:]]*\)/\1/p" "$file") + interpreter=$(basename "$interpreter_path") + + if [ "$given_interpreter" = "$interpreter" ] ; then + echo "$interpreter_path" + return 0 + fi + done + return 1 +} + +configure_debian() +{ + rootfs=$1 + hostname=$2 + num_tty=$3 + + # squeeze only has /dev/tty and /dev/tty0 by default, + # therefore creating missing device nodes for tty1-4. + for tty in $(seq 1 "$num_tty"); do + if [ ! -e "$rootfs/dev/tty$tty" ]; then + mknod "$rootfs/dev/tty$tty" c 4 "$tty" + fi + done + + # configure the inittab + cat < $rootfs/etc/inittab +id:3:initdefault: +si::sysinit:/etc/init.d/rcS +l0:0:wait:/etc/init.d/rc 0 +l1:1:wait:/etc/init.d/rc 1 +l2:2:wait:/etc/init.d/rc 2 +l3:3:wait:/etc/init.d/rc 3 +l4:4:wait:/etc/init.d/rc 4 +l5:5:wait:/etc/init.d/rc 5 +l6:6:wait:/etc/init.d/rc 6 +# Normally not reached, but fallthrough in case of emergency. +z6:6:respawn:/sbin/sulogin +1:2345:respawn:/sbin/getty 38400 console +$(for tty in $(seq 1 "$num_tty"); do echo "c${tty}:12345:respawn:/sbin/getty 38400 tty${tty} linux" ; done;) +p6::ctrlaltdel:/sbin/init 6 +p0::powerfail:/sbin/init 0 +EOF + + # symlink mtab + [ -e "$rootfs/etc/mtab" ] && rm "$rootfs/etc/mtab" + ln -s /proc/self/mounts "$rootfs/etc/mtab" + + # disable selinux in debian + mkdir -p "$rootfs/selinux" + echo 0 > "$rootfs/selinux/enforce" + + # configure the network using the dhcp + cat < $rootfs/etc/network/interfaces +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp +EOF + + # set the hostname + cat < $rootfs/etc/hostname +$hostname +EOF + + # reconfigure some services + + # but first reconfigure locales - so we get no noisy perl-warnings + if [ -z "$LANG" ] || echo $LANG | grep -E -q "^C(\..+)*$"; then + cat >> "$rootfs/etc/locale.gen" << EOF +en_US.UTF-8 UTF-8 +EOF + chroot "$rootfs" locale-gen en_US.UTF-8 UTF-8 + chroot "$rootfs" update-locale LANG=en_US.UTF-8 + else + encoding=$(echo "$LANG" | cut -d. -f2) + chroot "$rootfs" sed -e "s/^# \(${LANG} ${encoding}\)/\1/" \ + -i /etc/locale.gen 2> /dev/null + cat >> "$rootfs/etc/locale.gen" << EOF +$LANG $encoding +EOF + chroot "$rootfs" locale-gen "$LANG" "$encoding" + chroot "$rootfs" update-locale LANG="$LANG" + fi + + # remove pointless services in a container + chroot "$rootfs" /usr/sbin/update-rc.d -f checkroot.sh disable + chroot "$rootfs" /usr/sbin/update-rc.d -f umountfs disable + chroot "$rootfs" /usr/sbin/update-rc.d -f hwclock.sh disable + chroot "$rootfs" /usr/sbin/update-rc.d -f hwclockfirst.sh disable + + # generate new SSH keys + if [ -x "$rootfs/var/lib/dpkg/info/openssh-server.postinst" ]; then + cat > "$rootfs/usr/sbin/policy-rc.d" << EOF +#!/bin/sh +exit 101 +EOF + chmod +x "$rootfs/usr/sbin/policy-rc.d" + + if [ -f "$rootfs/etc/init/ssh.conf" ]; then + mv "$rootfs/etc/init/ssh.conf" "$rootfs/etc/init/ssh.conf.disabled" + fi + + rm -f "$rootfs/etc/ssh/"ssh_host_*key* + + DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot "$rootfs" /var/lib/dpkg/info/openssh-server.postinst configure + sed -i "s/root@$(hostname)/root@$hostname/g" "$rootfs/etc/ssh/"ssh_host_*.pub + + if [ -f "$rootfs/etc/init/ssh.conf.disabled" ]; then + mv "$rootfs/etc/init/ssh.conf.disabled" "$rootfs/etc/init/ssh.conf" + fi + + rm -f "$rootfs/usr/sbin/policy-rc.d" + fi + + # set initial timezone as on host + if [ -f /etc/timezone ]; then + cat /etc/timezone > "$rootfs/etc/timezone" + chroot "$rootfs" dpkg-reconfigure -f noninteractive tzdata + elif [ -f /etc/sysconfig/clock ]; then + . /etc/sysconfig/clock + echo "$ZONE" > "$rootfs/etc/timezone" + chroot "$rootfs" dpkg-reconfigure -f noninteractive tzdata + else + echo "Timezone in container is not configured. Adjust it manually." + fi + + if [ -n "$authkey" ]; then + local ssh_dir_path="${rootfs}/root/.ssh" + mkdir -p "$ssh_dir_path" + cp "$authkey" "${ssh_dir_path}/authorized_keys" + chmod 700 "$ssh_dir_path" + echo "Inserted SSH public key from '$authkey' into /root/.ssh/authorized_keys" + fi + + return 0 +} + +write_sourceslist() +{ + local rootfs="$1"; shift + local release="$1"; shift + local arch="$1"; shift + + local prefix="deb" + if [ -n "${arch}" ]; then + prefix="deb [arch=${arch}]" + fi + + if [ "$mainonly" = 1 ]; then + non_main='' + else + non_main=' contrib non-free' + fi + + cat >> "${rootfs}/etc/apt/sources.list" << EOF +${prefix} $MIRROR ${release} main${non_main} +EOF + + if [ "$release" != "unstable" -a "$release" != "sid" ]; then + cat >> "${rootfs}/etc/apt/sources.list" << EOF +${prefix} $SECURITY_MIRROR ${release}/updates main${non_main} +EOF + fi +} + +install_packages() +{ + local rootfs="$1"; shift + local packages="$*" + + chroot "${rootfs}" apt-get update + if [ -n "${packages}" ]; then + chroot "${rootfs}" apt-get install --force-yes -y --no-install-recommends ${packages} + fi +} + +configure_debian_systemd() +{ + path=$1 + rootfs=$2 + config=$3 + num_tty=$4 + + # just in case systemd is not installed + mkdir -p "${rootfs}/lib/systemd/system" + mkdir -p "${rootfs}/etc/systemd/system/getty.target.wants" + + # Fix getty-static-service as debootstrap does not install dbus + if [ -e "$rootfs//lib/systemd/system/getty-static.service" ] ; then + local tty_services + tty_services=$(for i in $(seq 2 "$num_tty"); do echo -n "getty@tty${i}.service "; done; ) + sed 's/ getty@tty.*/'" $tty_services "'/g' \ + "$rootfs/lib/systemd/system/getty-static.service" | \ + sed 's/\(tty2-tty\)[5-9]/\1'"${num_tty}"'/g' > "$rootfs/etc/systemd/system/getty-static.service" + fi + + # This function has been copied and adapted from lxc-fedora + rm -f "${rootfs}/etc/systemd/system/default.target" + chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/udev.service + chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/systemd-udevd.service + chroot "${rootfs}" ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target + # Setup getty service on the ttys we are going to allow in the + # default config. Number should match lxc.tty.max + ( cd "${rootfs}/etc/systemd/system/getty.target.wants" + for i in $(seq 1 "$num_tty") ; do ln -sf ../getty\@.service getty@tty"${i}".service; done ) + + # Since we use static-getty.target; we need to mask container-getty@.service generated by + # container-getty-generator, so we don't get multiple instances of agetty running. + # See https://github.com/lxc/lxc/issues/520 and https://github.com/lxc/lxc/issues/484 + ( cd "${rootfs}/etc/systemd/system/getty.target.wants" + for i in $(seq 0 "$num_tty"); do ln -sf /dev/null container-getty\@"${i}".service; done ) + + return 0 +} + +# Check if given path is in a btrfs partition +is_btrfs() +{ + [ -e "$1" -a "$(stat -f -c '%T' "$1")" = "btrfs" ] +} + +# Check if given path is the root of a btrfs subvolume +is_btrfs_subvolume() +{ + [ -d "$1" -a "$(stat -f -c '%T' "$1")" = "btrfs" -a "$(stat -c '%i' "$1")" -eq 256 ] +} + +try_mksubvolume() +{ + path=$1 + [ -d "$path" ] && return 0 + mkdir -p "$(dirname "$path")" + if which btrfs >/dev/null 2>&1 && is_btrfs "$(dirname "$path")"; then + btrfs subvolume create "$path" + else + mkdir -p "$path" + fi +} + +try_rmsubvolume() +{ + path=$1 + [ -d "$path" ] || return 0 + if which btrfs >/dev/null 2>&1 && is_btrfs_subvolume "$path"; then + btrfs subvolume delete "$path" + else + rm -rf "$path" + fi +} + +cleanup() +{ + try_rmsubvolume "$cache/partial-$release-$arch" + try_rmsubvolume "$cache/rootfs-$release-$arch" +} + +download_debian() +{ + case "$release" in + wheezy) + init=sysvinit + iproute=iproute + ;; + *) + init=init + iproute=iproute2 + ;; + esac + packages=\ +$init,\ +ifupdown,\ +locales,\ +dialog,\ +isc-dhcp-client,\ +netbase,\ +net-tools,\ +$iproute,\ +openssh-server + + cache=$1 + arch=$2 + release=$3 + interpreter="$4" + interpreter_path="$5" + + trap cleanup EXIT SIGHUP SIGINT SIGTERM + + # Create the cache + mkdir -p "$cache" + + # If debian-archive-keyring isn't installed, fetch GPG keys directly + releasekeyring=/usr/share/keyrings/debian-archive-keyring.gpg + if [ ! -f $releasekeyring ]; then + releasekeyring="$cache/archive-key.gpg" + case $release in + "wheezy") + gpgkeyname="archive-key-7.0" + ;; + *) + gpgkeyname="archive-key-8" + ;; + esac + wget https://ftp-master.debian.org/keys/${gpgkeyname}.asc -O - --quiet \ + | gpg --import --no-default-keyring --keyring="${releasekeyring}" + fi + # check the mini debian was not already downloaded + try_mksubvolume "$cache/partial-$release-$arch" + if [ $? -ne 0 ]; then + echo "Failed to create '$cache/partial-$release-$arch' directory" + return 1 + fi + + # download a mini debian into a cache + echo "Downloading debian minimal ..." + if [ "$interpreter" = "" ] ; then + debootstrap --verbose --variant=minbase --arch="$arch" \ + --include=$packages --keyring="${releasekeyring}" \ + "$release" "$cache/partial-$release-$arch" "$MIRROR" + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + else + debootstrap --foreign --verbose --variant=minbase --arch="$arch" \ + --include=$packages --keyring="${releasekeyring}" \ + "$release" "$cache/partial-$release-$arch" "$MIRROR" + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + mkdir -p "$(basename "$cache/partial-$release-$arch/$interpreter_path")" + cp "$interpreter" "$cache/partial-$release-$arch/$interpreter_path" + if [ $? -ne 0 ]; then + echo "failed to copy $interpreter to $cache/partial-$release-$arch/$interpreter_path" + return 1 + fi + chroot "$cache/partial-$release-$arch" debootstrap/debootstrap --second-stage + if [ $? -ne 0 ]; then + echo "failed to update the rootfs, aborting" + return 1 + fi + fi + + mv "$1/partial-$release-$arch" "$1/rootfs-$release-$arch" + echo "Download complete." + trap EXIT + trap SIGINT + trap SIGTERM + trap SIGHUP + + return 0 +} + +copy_debian() +{ + cache=$1 + arch=$2 + rootfs=$3 + release=$4 + + # make a local copy of the minidebian + echo -n "Copying rootfs to $rootfs..." + try_mksubvolume "$rootfs" + if which btrfs >/dev/null 2>&1 && \ + is_btrfs_subvolume "$cache/rootfs-$release-$arch" && \ + is_btrfs_subvolume "$rootfs"; then + realrootfs="$(dirname "$config")"/rootfs + [ "$rootfs" = "$realrootfs" ] || umount "$rootfs" || return 1 + btrfs subvolume delete "$realrootfs" || return 1 + btrfs subvolume snapshot "$cache/rootfs-$release-$arch" "$realrootfs" || return 1 + [ "$rootfs" = "$realrootfs" ] || mount --bind "$realrootfs" "$rootfs" || return 1 + else + rsync -SHaAX "$cache/rootfs-$release-$arch"/ $rootfs/ || return 1 + fi + return 0 +} + +install_debian() +{ + rootfs=$1 + release=$2 + arch=$3 + cache="$4/debian" + interpreter="$5" + interpreter_path="$6" + flushcache=$7 + mkdir -p $LOCALSTATEDIR/lock/subsys/ + ( + flock -x 9 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + if [ "$flushcache" -eq 1 ]; then + echo "Flushing cache..." + cleanup + fi + + echo "Checking cache download in $cache/rootfs-$release-$arch ... " + if [ ! -e "$cache/rootfs-$release-$arch" ]; then + download_debian "$cache" "$arch" "$release" "$interpreter" "$interpreter_path" + if [ $? -ne 0 ]; then + echo "Failed to download 'debian base'" + return 1 + fi + fi + + copy_debian "$cache" "$arch" "$rootfs" "$release" + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + + return 0 + + ) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian + + return $? +} + +copy_configuration() +{ + path=$1 + rootfs=$2 + hostname=$3 + arch=$4 + num_tty=$5 + + # Generate the configuration file + # if there is exactly one veth network entry, make sure it has an + # associated hwaddr. + nics=$(grep -ce '^lxc\.net\.0\.type[ \t]*=[ \t]*veth' "$path/config") + if [ "$nics" -eq 1 ]; then + grep -q "^lxc.net.0.hwaddr" "$path/config" || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" "$path/config" + fi + + ## Add all the includes + echo "" >> "$path/config" + echo "# Common configuration" >> "$path/config" + if [ -e "${LXC_TEMPLATE_CONFIG}/debian.common.conf" ]; then + echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.common.conf" >> "$path/config" + fi + if [ -e "${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" ]; then + echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" >> "$path/config" + fi + + ## Add the container-specific config + echo "" >> "$path/config" + echo "# Container specific configuration" >> "$path/config" + grep -q "^lxc.rootfs.path" "$path/config" 2> /dev/null || echo "lxc.rootfs.path = $rootfs" >> "$path/config" + + cat <> $path/config +lxc.tty.max = $num_tty +lxc.uts.name = $hostname +lxc.arch = $arch +lxc.pty.max = 1024 +EOF + + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +post_process() +{ + local rootfs="$1"; shift + local release="$1"; shift + local arch="$1"; shift + local hostarch="$1"; shift + local interpreter="$1"; shift + local packages="$*" + + # Disable service startup + cat > "${rootfs}/usr/sbin/policy-rc.d" << EOF +#!/bin/sh +exit 101 +EOF + chmod +x "${rootfs}/usr/sbin/policy-rc.d" + + # If the container isn't running a native architecture, setup multiarch + if [ "$interpreter" = "" -a "${arch}" != "${hostarch}" ]; then + # Test if dpkg supports multiarch + if ! chroot "$rootfs" dpkg --print-foreign-architectures 2>&1; then + chroot "$rootfs" dpkg --add-architecture "${hostarch}" + fi + fi + + # Write a new sources.list containing both native and multiarch entries + > "${rootfs}/etc/apt/sources.list" + if [ "$interpreter" != "" -a "${arch}" = "${hostarch}" ]; then + write_sourceslist "${rootfs}" "${release}" "${arch}" + else + write_sourceslist "${rootfs}" "${release}" + fi + + # Install Packages in container + if [ -n "${packages}" ]; then + local pack_list + pack_list="${packages//,/ }" + echo "Installing packages: ${pack_list}" + install_packages "${rootfs}" "${pack_list}" + fi + + # Re-enable service startup + rm "${rootfs}/usr/sbin/policy-rc.d" + + # end +} + +clean() +{ + cache=${LXC_CACHE_PATH:-"$LOCALSTATEDIR/cache/lxc/debian"} + + if [ ! -e "$cache" ]; then + exit 0 + fi + + # lock, so we won't purge while someone is creating a repository + ( + flock -x 9 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + + echo -n "Purging the download cache..." + rm --preserve-root --one-file-system -rf "$cache" && echo "Done." || exit 1 + exit 0 + + ) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian +} + +usage() +{ + cat < [-c|--clean] [-a|--arch=] [-r|--release=] + [--mirror=] [--security-mirror=] + [--package=] + [-I|--interpreter-path=] + [-F | --flush-cache] [-S|--auth-key=] + +Options : + + -h, --help print this help text + -p, --path=PATH directory where config and rootfs of this VM will be kept + -S, --auth-key=KEYFILE SSH public key to inject into the container as the root user. + -a, --arch=ARCH The container architecture. Can be one of: i686, x86_64, + amd64, armhf, armel, powerpc. Defaults to host arch. + -r, --release=RELEASE Debian release. Can be one of: wheezy, jessie, stretch, buster, sid. + Defaults to current stable. + --mirror=MIRROR Debian mirror to use during installation. Overrides the MIRROR + environment variable (see below). + --security-mirror=SECURITY_MIRROR + Debian mirror to use for security updates. Overrides the + SECURITY_MIRROR environment variable (see below). + --packages=PACKAGE_NAME1,PACKAGE_NAME2,... + List of additional packages to install. Comma separated, without space. + -c, --clean only clean up the cache and terminate + --enable-non-free include also Debian's contrib and non-free repositories. + -I|--interpreter-path=INTERPRETER-PATH + Path of the binfmt interpreter to copy to the rootfs + -F | --flush-cache Flush the debian release cache + +Environment variables: + + MIRROR The Debian package mirror to use. See also the --mirror switch above. + Defaults to '$MIRROR' + SECURITY_MIRROR The Debian package security mirror to use. See also the --security-mirror switch above. + Defaults to '$SECURITY_MIRROR' + +EOF + return 0 +} + +options=$(getopt -o hp:n:a:r:cI:FS: -l arch:,auth-key:,clean,help,enable-non-free,mirror:,name:,packages:,path:,release:,rootfs:,security-mirror:,interpreter-path:,flush-cache -- "$@") +if [ $? -ne 0 ]; then + usage "$(basename "$0")" + exit 1 +fi +eval set -- "$options" + +littleendian=$(lscpu | grep '^Byte Order' | grep -q Little && echo yes) + +arch=$(uname -m) +if [ "$arch" = "i686" ]; then + arch="i386" +elif [ "$arch" = "x86_64" ]; then + arch="amd64" +elif [ "$arch" = "armv7l" ]; then + arch="armhf" +elif [ "$arch" = "aarch64" ]; then + arch="arm64" +elif [ "$arch" = "ppc" ]; then + arch="powerpc" +elif [ "$arch" = "ppc64le" ]; then + arch="ppc64el" +elif [ "$arch" = "mips" -a "$littleendian" = "yes" ]; then + arch="mipsel" +elif [ "$arch" = "mips64" -a "$littleendian" = "yes" ]; then + arch="mips64el" +fi +hostarch=$arch +mainonly=1 +flushcache=0 + +while true +do + case "$1" in + -h|--help) usage "$0" && exit 1;; + --) shift 1; break ;; + + -a|--arch) arch=$2; shift 2;; + -S|--auth-key) authkey=$2; shift 2;; + -I|--interpreter-path) + interpreter="$2"; shift 2;; + -c|--clean) clean=1; shift 1;; + --enable-non-free) mainonly=0; shift 1;; + --mirror) MIRROR=$2; shift 2;; + -n|--name) name=$2; shift 2;; + --packages) packages=$2; shift 2;; + -p|--path) path=$2; shift 2;; + -r|--release) release=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; + --security-mirror) SECURITY_MIRROR=$2; shift 2;; + -F|--flush-cache) flushcache=1; shift 1;; + *) break ;; + esac +done + +if [ ! -z "$clean" -a -z "$path" ]; then + clean || exit 1 + exit 0 +fi + +if [ "$arch" = "i686" ]; then + arch=i386 +fi + +if [ "$arch" = "x86_64" ]; then + arch=amd64 +fi + +if [ "$interpreter" = "" ] ; then + if [ $hostarch = "i386" -a $arch = "amd64" ]; then + echo "can't create $arch container on $hostarch" + exit 1 + fi + + if [ $hostarch = "armhf" -o $hostarch = "armel" ] && \ + [ $arch != "armhf" -a $arch != "armel" ]; then + echo "can't create $arch container on $hostarch" + exit 1 + fi + + if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then + echo "can't create $arch container on $hostarch" + exit 1 + fi + + if [ $hostarch = "mips" -a $arch != "mips" ] || \ + [ $hostarch = "mipsel" -a $arch != "mipsel" ] || \ + [ $hostarch = "mips64" -a $arch != "mips" -a $arch != "mips64" ] || \ + [ $hostarch = "mips64el" -a $arch != "mipsel" -a $arch != "mips64el" ]; then + echo "can't create $arch container on $hostarch" + exit 1 + fi +else + if ! file -b "${interpreter}" |grep -q "statically linked" ; then + echo "'${interpreter}' must be statically linked" 1>&2 + exit 1 + fi + interpreter_path=$(find_interpreter "$interpreter") + if [ $? -ne 0 ] ; then + echo "no binfmt interpreter using $(basename "$interpreter")" 1>&2 + exit 1 + fi +fi + +type debootstrap +if [ $? -ne 0 ]; then + echo "'debootstrap' command is missing" + exit 1 +fi + +if [ -z "$path" ]; then + echo "'path' parameter is required" + exit 1 +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -n "$authkey" ]; then + if [ ! -f "$authkey" ]; then + echo "SSH keyfile '$authkey' not found" + exit 1 + fi + # This is mostly to prevent accidental uage of the private key instead + # of the public key. + if [ "${authkey: -4}" != ".pub" ]; then + echo "SSH keyfile '$authkey' does not end with '.pub'" + exit 1 + fi +fi + +release=${release:-stable} +permanent_releases=('stable' 'testing' 'sid' 'unstable') +if [[ ! "${permanent_releases[*]}" =~ (^|[^[:alpha:]])$release([^[:alpha:]]|$) ]]; then + if ! wget "${MIRROR}/dists/${release}/Release" -O /dev/null 2> /dev/null; then + echo "Invalid release ${release} (not found in mirror)" + exit 1 + fi +fi + +# detect rootfs +config="$path/config" +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs.path' "$config" 2> /dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path[ \t]+=/{ print $2 }' "$config") + else + rootfs=$path/rootfs + fi +fi + +# determine the number of ttys - default is 4 +if grep -q '^lxc.tty.max' "$config" 2> /dev/null ; then + num_tty=$(awk -F= '/^lxc.tty.max[ \t]+=/{ print $2 }' "$config") +else + num_tty=4 +fi + +install_debian "$rootfs" "$release" "$arch" "$LXC_CACHE_PATH" "$interpreter" "$interpreter_path" "$flushcache" +if [ $? -ne 0 ]; then + echo "failed to install debian" + exit 1 +fi + +configure_debian "$rootfs" "$name" $num_tty +if [ $? -ne 0 ]; then + echo "failed to configure debian for a container" + exit 1 +fi + +copy_configuration "$path" "$rootfs" "$name" $arch $num_tty +if [ $? -ne 0 ]; then + echo "failed write configuration file" + exit 1 +fi + +configure_debian_systemd "$path" "$rootfs" "$config" $num_tty + +post_process "${rootfs}" "${release}" ${arch} ${hostarch} "${interpreter}" "${packages}" + +if [ ! -z "$clean" ]; then + clean || exit 1 + exit 0 +fi diff --git a/templates/lxc-fedora-legacy.in b/templates/lxc-fedora-legacy.in new file mode 100644 index 0000000..3ef3415 --- /dev/null +++ b/templates/lxc-fedora-legacy.in @@ -0,0 +1,1495 @@ +#!/bin/bash + +# +# template script for generating fedora container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# Daniel Lezcano +# Ramez Hanna +# Michael H. Warfield + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#Configurations +default_path=@LXCPATH@ + +# Some combinations of the tuning knobs below do not exactly make sense. +# but that's ok. +# +# If the "root_password" is non-blank, use it, else set a default. +# This can be passed to the script as an environment variable and is +# set by a shell conditional assignment. Looks weird but it is what it is. +# +# If the root password contains a ding ($) then try to expand it. +# That will pick up things like ${name} and ${RANDOM}. +# If the root password contains more than 3 consecutive X's, pass it as +# a template to mktemp and take the result. +# +# If root_display_password = yes, display the temporary root password at exit. +# If root_store_password = yes, store it in the configuration directory +# If root_prompt_password = yes, invoke "passwd" to force the user to change +# the root password after the container is created. +# If root_expire_password = yes, you will be prompted to change the root +# password at the first login. +# +# These are conditional assignments... The can be overridden from the +# preexisting environment variables... +# +# Make sure this is in single quotes to defer expansion to later! +# :{root_password='Root-${name}-${RANDOM}'} +: ${root_password='Root-${name}-XXXXXX'} + +# Now, it doesn't make much sense to display, store, and force change +# together. But, we gotta test, right??? +: ${root_display_password='no'} +: ${root_store_password='yes'} +# Prompting for something interactive has potential for mayhem +# with users running under the API... Don't default to "yes" +: ${root_prompt_password='no'} + +# Expire root password? Default to yes, but can be overridden from +# the environment variable +: ${root_expire_password='yes'} + +# These are only going into comments in the resulting config... +lxc_network_type=veth +lxc_network_link=lxcbr0 + +# is this fedora? +# Alow for weird remixes like the Raspberry Pi +# +# Use the Mitre standard CPE identifier for the release ID if possible... +# This may be in /etc/os-release or /etc/system-release-cpe. We +# should be able to use EITHER. Give preference to /etc/os-release for now. + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +if [ -e /etc/os-release ] +then +# This is a shell friendly configuration file. We can just source it. +# What we're looking for in here is the ID, VERSION_ID and the CPE_NAME + . /etc/os-release + echo "Host CPE ID from /etc/os-release: ${CPE_NAME}" +fi + +if [ "${CPE_NAME}" = "" -a -e /etc/system-release-cpe ] +then + CPE_NAME=$(head -n1 /etc/system-release-cpe) + CPE_URI=$(expr ${CPE_NAME} : '\([^:]*:[^:]*\)') + if [ "${CPE_URI}" != "cpe:/o" ] + then + CPE_NAME= + else + echo "Host CPE ID from /etc/system-release-cpe: ${CPE_NAME}" + # Probably a better way to do this but sill remain posix + # compatible but this works, shrug... + # Must be nice and not introduce convenient bashisms here. + ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:\([^:]*\)') + VERSION_ID=$(expr ${CPE_NAME} : '[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\)') + fi +fi + +if [ "${CPE_NAME}" != "" -a "${ID}" = "fedora" -a "${VERSION_ID}" != "" ] +then + fedora_host_ver=${VERSION_ID} + is_fedora=true +elif [ -e /etc/redhat-release ] +then + # Only if all other methods fail, try to parse the redhat-release file. + fedora_host_ver=$( sed -e '/^Fedora /!d' -e 's/Fedora.*\srelease\s*\([0-9][0-9]*\)\s.*/\1/' < /etc/redhat-release ) + if [ "$fedora_host_ver" != "" ] + then + is_fedora=true + fi +fi + +configure_fedora() +{ + + # disable selinux in fedora + mkdir -p $rootfs_path/selinux + echo 0 > $rootfs_path/selinux/enforce + + # Also kill it in the /etc/selinux/config file if it's there... + if [[ -f $rootfs_path/etc/selinux/config ]] + then + sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' $rootfs_path/etc/selinux/config + fi + + # Nice catch from Dwight Engen in the Oracle template. + # Wantonly plagerized here with much appreciation. + if [ -f $rootfs_path/usr/sbin/selinuxenabled ]; then + mv $rootfs_path/usr/sbin/selinuxenabled $rootfs_path/usr/sbin/selinuxenabled.lxcorig + ln -s /bin/false $rootfs_path/usr/sbin/selinuxenabled + fi + + # This is a known problem and documented in RedHat bugzilla as relating + # to a problem with auditing enabled. This prevents an error in + # the container "Cannot make/remove an entry for the specified session" + sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/login + sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/sshd + + if [ -f ${rootfs_path}/etc/pam.d/crond ] + then + sed -i '/^session.*pam_loginuid.so/s/^session/# session/' ${rootfs_path}/etc/pam.d/crond + fi + + # In addition to disabling pam_loginuid in the above config files + # we'll also disable it by linking it to pam_permit to catch any + # we missed or any that get installed after the container is built. + # + # Catch either or both 32 and 64 bit archs. + if [ -f ${rootfs_path}/lib/security/pam_loginuid.so ] + then + ( cd ${rootfs_path}/lib/security/ + mv pam_loginuid.so pam_loginuid.so.disabled + ln -s pam_permit.so pam_loginuid.so + ) + fi + + if [ -f ${rootfs_path}/lib64/security/pam_loginuid.so ] + then + ( cd ${rootfs_path}/lib64/security/ + mv pam_loginuid.so pam_loginuid.so.disabled + ln -s pam_permit.so pam_loginuid.so + ) + fi + + # Set default localtime to the host localtime if not set... + if [ -e /etc/localtime -a ! -e ${rootfs_path}/etc/localtime ] + then + # if /etc/localtime is a symlink, this should preserve it. + cp -a /etc/localtime ${rootfs_path}/etc/localtime + fi + + # Deal with some dain bramage in the /etc/init.d/halt script. + # Trim it and make it our own and link it in before the default + # halt script so we can intercept it. This also preventions package + # updates from interferring with our interferring with it. + # + # There's generally not much in the halt script that useful but what's + # in there from resetting the hardware clock down is generally very bad. + # So we just eliminate the whole bottom half of that script in making + # ourselves a copy. That way a major update to the init scripts won't + # trash what we've set up. + # + # This is mostly for legacy distros since any modern systemd Fedora + # release will not have this script so we won't try to intercept it. + if [ -f ${rootfs_path}/etc/init.d/halt ] + then + sed -e '/hwclock/,$d' \ + < ${rootfs_path}/etc/init.d/halt \ + > ${rootfs_path}/etc/init.d/lxc-halt + + echo '$command -f' >> ${rootfs_path}/etc/init.d/lxc-halt + chmod 755 ${rootfs_path}/etc/init.d/lxc-halt + + # Link them into the rc directories... + ( + cd ${rootfs_path}/etc/rc.d/rc0.d + ln -s ../init.d/lxc-halt S00lxc-halt + cd ${rootfs_path}/etc/rc.d/rc6.d + ln -s ../init.d/lxc-halt S00lxc-reboot + ) + fi + + # configure the network using the dhcp + cat < ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0 +DEVICE=eth0 +BOOTPROTO=dhcp +ONBOOT=yes +HOSTNAME=${utsname} +DHCP_HOSTNAME=\`hostname\` +NM_CONTROLLED=no +TYPE=Ethernet +MTU=${MTU} +EOF + + # set the hostname + cat < ${rootfs_path}/etc/sysconfig/network +NETWORKING=yes +HOSTNAME=${utsname} +EOF + + # set hostname on systemd Fedora systems + if [ $release -gt 14 ]; then + echo "${utsname}" > ${rootfs_path}/etc/hostname + fi + + # set minimal hosts + cat < $rootfs_path/etc/hosts +127.0.0.1 localhost.localdomain localhost $utsname +::1 localhost6.localdomain6 localhost6 +EOF + + # These mknod's really don't make any sense with modern releases of + # Fedora with systemd, devtmpfs, and autodev enabled. They are left + # here for legacy reasons and older releases with upstart and sysv init. + dev_path="${rootfs_path}/dev" + rm -rf $dev_path + mkdir -p $dev_path + mknod -m 666 ${dev_path}/null c 1 3 + mknod -m 666 ${dev_path}/zero c 1 5 + mknod -m 666 ${dev_path}/random c 1 8 + mknod -m 666 ${dev_path}/urandom c 1 9 + mkdir -m 755 ${dev_path}/pts + mkdir -m 1777 ${dev_path}/shm + mknod -m 666 ${dev_path}/tty c 5 0 + mknod -m 666 ${dev_path}/tty0 c 4 0 + mknod -m 666 ${dev_path}/tty1 c 4 1 + mknod -m 666 ${dev_path}/tty2 c 4 2 + mknod -m 666 ${dev_path}/tty3 c 4 3 + mknod -m 666 ${dev_path}/tty4 c 4 4 + mknod -m 600 ${dev_path}/console c 5 1 + mknod -m 666 ${dev_path}/full c 1 7 + mknod -m 600 ${dev_path}/initctl p + mknod -m 666 ${dev_path}/ptmx c 5 2 + + # setup console and tty[1-4] for login. note that /dev/console and + # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and + # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. + # lxc will maintain these links and bind mount ptys over /dev/lxc/* + # since lxc.tty.dir is specified in the config. + + # allow root login on console, tty[1-4], and pts/0 for libvirt + echo "# LXC (Linux Containers)" >>${rootfs_path}/etc/securetty + echo "lxc/console" >>${rootfs_path}/etc/securetty + echo "lxc/tty1" >>${rootfs_path}/etc/securetty + echo "lxc/tty2" >>${rootfs_path}/etc/securetty + echo "lxc/tty3" >>${rootfs_path}/etc/securetty + echo "lxc/tty4" >>${rootfs_path}/etc/securetty + echo "# For libvirt/Virtual Machine Monitor" >>${rootfs_path}/etc/securetty + echo "pts/0" >>${rootfs_path}/etc/securetty + + if [ ${root_display_password} = "yes" ] + then + echo "Setting root password to '$root_password'" + fi + if [ ${root_store_password} = "yes" ] + then + touch ${config_path}/tmp_root_pass + chmod 600 ${config_path}/tmp_root_pass + echo ${root_password} > ${config_path}/tmp_root_pass + echo "Storing root password in '${config_path}/tmp_root_pass'" + fi + + echo "root:$root_password" | chroot $rootfs_path chpasswd + + if [ ${root_expire_password} = "yes" ] + then + # Also set this password as expired to force the user to change it! + chroot $rootfs_path passwd -e root + fi + + # specifying this in the initial packages doesn't always work. + # Even though it should have... + echo "installing fedora-release package" + mount -o bind /dev ${rootfs_path}/dev + mount -t proc proc ${rootfs_path}/proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf ${rootfs_path}/etc/ + # Rebuild the rpm database based on the target rpm version... + rm -f ${rootfs_path}/var/lib/rpm/__db* + chroot ${rootfs_path} rpm --rebuilddb + chroot ${rootfs_path} yum -y install fedora-release + + if [[ ! -e ${rootfs_path}/sbin/NetworkManager ]] + then + # NetworkManager has not been installed. Use the + # legacy chkconfig command to enable the network startup + # scripts in the container. + chroot ${rootfs_path} chkconfig network on + fi + + umount ${rootfs_path}/proc + umount ${rootfs_path}/dev + + # silence some needless startup errors + touch ${rootfs_path}/etc/fstab + + # give us a console on /dev/console + sed -i 's/ACTIVE_CONSOLES=.*$/ACTIVE_CONSOLES="\/dev\/console \/dev\/tty[1-4]"/' \ + ${rootfs_path}/etc/sysconfig/init + + return 0 +} + +configure_fedora_init() +{ + sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit + sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit + # don't mount devpts, for pete's sake + sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.sysinit + sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.d/rc.sysinit + chroot ${rootfs_path} chkconfig udev-post off + chroot ${rootfs_path} chkconfig network on + + if [ -d ${rootfs_path}/etc/init ] + then + # This is to make upstart honor SIGPWR. Should do no harm + # on systemd systems and some systems may have both. + cat <${rootfs_path}/etc/init/power-status-changed.conf +# power-status-changed - shutdown on SIGPWR +# +start on power-status-changed + +exec /sbin/shutdown -h now "SIGPWR received" +EOF + fi +} + +configure_fedora_systemd() +{ + rm -f ${rootfs_path}/etc/systemd/system/default.target + touch ${rootfs_path}/etc/fstab + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/udev.service + chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target + # Make systemd honor SIGPWR + chroot ${rootfs_path} ln -s /usr/lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target + + # if desired, prevent systemd from over-mounting /tmp with tmpfs + if [ $masktmp -eq 1 ]; then + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/tmp.mount + fi + + #dependency on a device unit fails it specially that we disabled udev + # sed -i 's/After=dev-%i.device/After=/' ${rootfs_path}/lib/systemd/system/getty\@.service + # + # Actually, the After=dev-%i.device line does not appear in the + # Fedora 17 or Fedora 18 systemd getty\@.service file. It may be left + # over from an earlier version and it's not doing any harm. We do need + # to disable the "ConditionalPathExists=/dev/tty0" line or no gettys are + # started on the ttys in the container. Lets do it in an override copy of + # the service so it can still pass rpm verifies and not be automatically + # updated by a new systemd version. -- mhw /\/\|=mhw=|\/\/ + + sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ + -e 's/After=dev-%i.device/After=/' \ + < ${rootfs_path}/lib/systemd/system/getty\@.service \ + > ${rootfs_path}/etc/systemd/system/getty\@.service + # Setup getty service on the 4 ttys we are going to allow in the + # default config. Number should match lxc.tty + ( cd ${rootfs_path}/etc/systemd/system/getty.target.wants + for i in 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${i}.service; done ) +} + +### BEGIN Bootstrap Environment Code... Michael H. Warfield /\/\|=mhw=|\/\/ + +# Ok... Heads up. If you're reading these comments, you're either a +# template owner or someone wondering how the hell I did this (or, worse, +# someone in the future trying to maintain it). This code is slightly +# "evil coding bastard" code with one significant hack / dirty trick +# that you would probably miss just reading the code below. I'll mark +# it out with comments. +# +# Because of what this code does, it deserves a lot of comments so people +# can understand WHY I did it this way... +# +# Ultimate Objective - Build a Fedora container on a host system which does +# not have a (complete compatible) version of rpm and/or yum. That basically +# means damn near any distro other than Fedora and Ubuntu (which has rpm and +# yum available). Only requirements for this function are rsync and +# squashfs available to the kernel. If you don't have those, why are you +# even attempting to build containers? +# +# Challenge for this function - Bootstrap a Fedora install bootstrap +# run time environment which has all the pieces to run rpm and yum and +# from which we can build targets containers even where the host system +# has no support for rpm, yum, or fedora. +# +# Steps: +# Stage 0 - Download a Fedora LiveOS squashfs core (netinst core). +# Stage 1 - Extract filesystem from Stage 0 and update to full rpm & yum +# Stage 2 - Use Stage 1 to build a rootfs with python, rpm, and yum. +# +# Stage 2 becomes our bootstrap file system which can be cached +# and then used to build other arbitrary vesions of Fedora of a +# given architecture. Note that this only has to run once for +# Fedora on a given architecture since rpm and yum can build other +# versions. We'll arbitrarily pick Fedora 20 to build this. This +# will need to change as time goes on. + +# Programmers Note... A future fall back may be to download the netinst +# iso image instead of the LiveOS squasfs image and work from that. +# That may be more general but will introduce another substep +# (mounting the iso) to the stage0 setup. + +# This system is designed to be as autonomous as possible so all whitelists +# and controls are self-contained. + +# Initial testing - Whitelist nobody. Build for everybody... +# Initial deployment - Whitelist Fedora. +# Long term - Whitelist Fedora, Debian, Ubuntu, CentOs, Scientific, and NST. + +# List of distros which do not (should not) need a bootstrap (but we will test +# for rpm and yum none the less... OS SHOULD be taken from CPE values but +# Debian / Ubuntu doesn't support CPE yet. + +# BOOTSTRAP_WHITE_LIST="" +BOOTSTRAP_WHITE_LIST="fedora" +# BOOTSTRAP_WHITE_LIST="fedora debian ubuntu centos scientific sl nst" + +BOOTSTRAP=0 +BOOTSTRAP_DIR= +BOOTSTRAP_CHROOT= + +fedora_get_bootstrap() +{ + echo "Bootstrap Environment testing..." + + WHITE_LISTED=1 + + # We need rpm. No rpm - not possible to white list... + if ! which rpm > /dev/null 2>&1 + then + WHITE_LISTED=0 + fi + + # We need yum No yum - not possible to white list... + if ! which yum > /dev/null 2>&1 + then + WHITE_LISTED=0 + fi + + if [[ ${WHITE_LISTED} != 0 ]] + then + for OS in ${BOOTSTRAP_WHITE_LIST} + do + if [[ ${ID} = ${OS} ]] + then + echo " +OS ${ID} is whitelisted. Installation Bootstrap Environment not required. +" + return 0; + fi + done + fi + + echo " +Fedora Installation Bootstrap Build..." + + if ! which rsync > /dev/null 2>&1 + then + echo " +Unable to locate rsync. Cravely bailing out before even attempting to build +an Installation Bootstrap Please install rsync and then rerun this process. +" + + return 255 + fi + + [[ -d ${cache_base} ]] || mkdir -p ${cache_base} + + cd ${cache_base} + + # We know we don't have a cache directory of this version or we + # would have never reached this code to begin with. But we may + # have another Fedora cache directory from which we could run... + # We'll give a preference for close matches preferring higher over + # lower - which makes for really ugly code... + + # Is this a "bashism" that will need cleaning up???? + BOOTSTRAP_LIST="$(( $release + 1 ))/rootfs $(( $release - 1 ))/rootfs \ +$(( $release + 2 ))/rootfs $(( $release - 2 ))/rootfs \ +$(( $release + 3 ))/rootfs $(( $release - 3 ))/rootfs \ +bootstrap" + + for bootstrap in ${BOOTSTRAP_LIST} + do + if [[ -d ${bootstrap} ]] + then + echo " +Existing Bootstrap found. Testing..." + + mount -o bind /dev ${bootstrap}/dev + mount -t proc proc ${bootstrap}/proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf ${bootstrap}/etc/ + rm -f ${bootstrap}/var/lib/rpm/__db* + chroot ${bootstrap} rpm --rebuilddb + chroot ${bootstrap} yum -y update + RC=$? + umount ${bootstrap}/proc + umount ${bootstrap}/dev + + if [[ 0 == ${RC} ]] + then + BOOTSTRAP=1 + BOOTSTRAP_DIR="${cache_base}/${bootstrap}" + BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} " + BOOTSTRAP_INSTALL_ROOT=/run/install + + echo " +Functional Installation Bootstrap exists and appears to be completed. +Will use existing Bootstrap: ${BOOTSTRAP_DIR} +" + return 0 + fi + echo " +Installation Bootstrap in ${BOOTSTRAP_DIR} exists +but appears to be non-functional. Skipping... It should be removed. +" + fi + done + + TMP_BOOTSTRAP_DIR=$( mktemp -d --tmpdir=${cache_base} bootstrap_XXXXXX ) + + cd ${TMP_BOOTSTRAP_DIR} + + mkdir squashfs stage0 stage1 bootstrap + +### Stage 0 setup. +# Download the LiveOS squashfs image +# mount image to "squashfs" +# mount contained LiveOS to stage0 + +# We're going to use the archives.fedoraproject.org mirror for the initial stages... +# 1 - It's generally up to date and complete +# 2 - It's has high bandwidth access +# 3 - It supports rsync and wildcarding (and we need both) +# 4 - Not all the mirrors carry the LiveOS images + + if [[ ! -f ../LiveOS/squashfs.img ]] + then + echo " +Downloading stage 0 LiveOS squashfs file system from archives.fedoraproject.org... +Have a beer or a cup of coffee. This will take a bit (~300MB). +" + sleep 3 # let him read it... + + # Right now, we are using Fedora 20 for the inial bootstrap. + # We could make this the "current" Fedora rev (F > 15). + + rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/LiveOS . + + if [[ 0 == $? ]] + then + echo "Download of squashfs image complete." + mv LiveOS .. + else + echo " +Download of squashfs image failed. +" + return 255 + fi + else + echo "Using cached stage 0 LiveOS squashfs file system." + fi + + mount -o loop ../LiveOS/squashfs.img squashfs + + if [[ $? != 0 ]] + then + echo " +Mount of LiveOS squashfs image failed! You mush have squashfs support +available to mount image. Unable to continue. Correct and retry +process later! LiveOS image not removed. Process may be rerun +without penalty of downloading LiveOS again. If LiveOS is corrupt, +remove ${cache_base}/LiveOS before rerunning to redownload. +" + return 255 + fi + + mount -o loop squashfs/LiveOS/rootfs.img stage0 + + if [[ $? != 0 ]] + then + echo " +Mount of LiveOS stage0 rootfs image failed! LiveOS download may be corrupt. +Remove ${cache_base}/LiveOS to force a new download or +troubleshoot cached image and then rerun process. +" + return 255 + fi + + +### Stage 1 setup. +# Copy stage0 (which is ro) to stage1 area (rw) for modification. +# Unmount stage0 mounts - we're done with stage 0 at this point. +# Download our rpm and yum rpm packages. +# Force install of rpm and yum into stage1 image (dirty hack!) + + echo "Stage 0 complete, building Stage 1 image... +This will take a couple of minutes. Patience..." + + echo "Creating Stage 1 r/w copy of r/o Stage 0 squashfs image from LiveOS." + + rsync -aAHS stage0/. stage1/ + + umount stage0 + umount squashfs + + cd stage1 + + # Setup stage1 image with pieces to run installs... + + + mount -o bind /dev dev + mount -t proc proc proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf etc/ + + mkdir run/install + + echo "Updating Stage 1 image with full rpm and yum packages" + + # Retrieve our 2 rpm packages we need to force down the throat + # of this LiveOS image we're camped out on. This is the beginning + # of the butt ugly hack. Look close or you may missing it... + + rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/r/rpm-[0-9]* \ + ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/y/yum-[0-9]* . + + # And here it is... + # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! + chroot . rpm -ivh --nodeps rpm-* yum-* + # Did you catch it? + + # The LiveOS image contains rpm (but not rpmdb) and yum (but not + # yummain.py - What the hell good does yum do with no + # yummain.py?!?! - Sigh...). It contains all the supporting + # pieces but the rpm database has not be initialized and it + # doesn't know all the dependences (seem to) have been met. + # So we do a "--nodeps" rpm install in the chrooted environment + # to force the installation of the full rpm and yum packages. + # + # For the purists - Yes, I know the rpm database is wildly out + # of whack now. That's why this is a butt ugly hack / dirty trick. + # But, this is just the stage1 image that we are going to discard as + # soon as the stage2 image is built, so we don't care. All we care + # is that the stage2 image ends up with all the pieces it need to + # run yum and rpm and that the stage2 rpm database is coherent. + # + # NOW we can really go to work! + +### Stage 2 setup. +# Download our Fedora Release rpm packages. +# Install fedora-release into bootstrap to initialize fs and databases. +# Install rpm, and yum into bootstrap image using yum + + echo "Stage 1 creation complete. Building stage 2 Installation Bootstrap" + + mount -o bind ../bootstrap run/install + rsync -av ${mirrorurl}/fedora/linux/releases/20/Fedora/$basearch/os/Packages/f/fedora-release-20* . + + # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! + chroot . rpm --root /run/install --nodeps -ivh fedora-release-* + + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" ./run/install/etc/yum.repos.d/* + + chroot . yum -y --nogpgcheck --installroot /run/install install python rpm yum + + umount run/install + umount proc + umount dev + +# That's it! We should now have a viable installation BOOTSTRAP in +# bootstrap We'll do a yum update in that to verify and then +# move it to the cache location before cleaning up. + + cd ../bootstrap + mount -o bind /dev dev + mount -t proc proc proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf etc/ + + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" ./etc/yum.repos.d/* + + chroot . yum -y update + + RC=$? + + umount proc + umount dev + + cd .. + + if [[ ${RC} != 0 ]] + then + echo " +Build of Installation Bootstrap failed. Temp directory +not removed so it can be investigated. +" + return 255 + fi + + # We know have a working run time environment in rootfs... + mv bootstrap .. + cd .. + rm -rf ${TMP_BOOTSTRAP_DIR} + + echo " +Build of Installation Bootstrap complete! We now return you to your +normally scheduled template creation. +" + + BOOTSTRAP=1 + BOOTSTRAP_DIR="${cache_base}/bootstrap" + BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} " + BOOTSTRAP_INSTALL_ROOT=/run/install + + return 0 +} + + +fedora_bootstrap_mounts() +{ + if [[ ${BOOTSTRAP} -ne 1 ]] + then + return 0 + fi + + BOOTSTRAP_CHROOT="chroot ${BOOTSTRAP_DIR} " + + echo "Mounting Bootstrap mount points" + + [[ -d ${BOOTSTRAP_DIR}/run/install ]] || mkdir -p ${BOOTSTRAP_DIR}/run/install + + mount -o bind ${INSTALL_ROOT} ${BOOTSTRAP_DIR}/run/install + mount -o bind /dev ${BOOTSTRAP_DIR}/dev + mount -t proc proc ${BOOTSTRAP_DIR}/proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf ${BOOTSTRAP_DIR}/etc/ +} + +fedora_bootstrap_umounts() +{ + if [[ ${BOOTSTRAP} -ne 1 ]] + then + return 0 + fi + + umount ${BOOTSTRAP_DIR}/proc + umount ${BOOTSTRAP_DIR}/dev + umount ${BOOTSTRAP_DIR}/run/install +} + + +# This is the code to create the initial roofs for Fedora. It may +# require a run time environment by calling the routines above... + +download_fedora() +{ + + # check the mini fedora was not already downloaded + INSTALL_ROOT=$cache/partial + mkdir -p $INSTALL_ROOT + if [ $? -ne 0 ]; then + echo "Failed to create '$INSTALL_ROOT' directory" + return 1 + fi + + # download a mini fedora into a cache + echo "Downloading fedora minimal ..." + + # These will get changed if it's decided that we need a + # boostrap environment (can not build natively). These + # are the defaults for the non-boostrap (native) mode. + + BOOTSTRAP_INSTALL_ROOT=${INSTALL_ROOT} + BOOTSTRAP_CHROOT= + BOOTSTRAP_DIR= + + PKG_LIST="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils fedora-release" + MIRRORLIST_URL="http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-$release&arch=$basearch" + + if [[ ${release} -lt 17 ]] + then + # The reflects the move of db_dump and db_load from db4_utils to + # libdb_utils in Fedora 17 and above and it's inclusion as a dep... + # Prior to Fedora 11, we need to explicitly include it! + PKG_LIST="${PKG_LIST} db4-utils" + fi + + if [[ ${release} -ge 21 ]] + then + # Since Fedora 21, a separate fedora-repos package is needed. + # Before, the information was conained in fedora-release. + PKG_LIST="${PKG_LIST} fedora-repos" + fi + + DOWNLOAD_OK=no + + # We're splitting the old loop into two loops plus a directory retrival. + # First loop... Try and retrive a mirror list with retries and a slight + # delay between attempts... + for trynumber in 1 2 3 4; do + [ $trynumber != 1 ] && echo "Trying again..." + # This code is mildly "brittle" in that it assumes a certain + # page format and parsing HTML. I've done worse. :-P + MIRROR_URLS=$(curl -s -S -f "$MIRRORLIST_URL" | sed -e '/^http:/!d' -e '2,6!d') + if [ $? -eq 0 ] && [ -n "$MIRROR_URLS" ] + then + break + fi + + echo "Failed to get a mirror on try $trynumber" + sleep 3 + done + + # This will fall through if we didn't get any URLS above + for MIRROR_URL in ${MIRROR_URLS} + do + if [ "$release" -gt "16" ]; then + RELEASE_URL="$MIRROR_URL/Packages/f" + else + RELEASE_URL="$MIRROR_URL/Packages/" + fi + + echo "Fetching release rpm name from $RELEASE_URL..." + # This code is mildly "brittle" in that it assumes a certain directory + # page format and parsing HTML. I've done worse. :-P + RELEASE_RPM=$(curl -L -f "$RELEASE_URL" | sed -e "/fedora-release-${release}-/!d" -e 's/.*.*//' ) + if [ $? -ne 0 -o "${RELEASE_RPM}" = "" ]; then + echo "Failed to identify fedora release rpm." + continue + fi + + echo "Fetching fedora release rpm from ${RELEASE_URL}/${RELEASE_RPM}......" + curl -L -f "${RELEASE_URL}/${RELEASE_RPM}" > ${INSTALL_ROOT}/${RELEASE_RPM} + if [ $? -ne 0 ]; then + echo "Failed to download fedora release rpm ${RELEASE_RPM}." + continue + fi + + # F21 and newer need fedora-repos in addition to fedora-release. + if [ "$release" -ge "21" ]; then + echo "Fetching repos rpm name from $RELEASE_URL..." + REPOS_RPM=$(curl -L -f "$RELEASE_URL" | sed -e "/fedora-repos-${release}-/!d" -e 's/.*.*//' ) + if [ $? -ne 0 -o "${REPOS_RPM}" = "" ]; then + echo "Failed to identify fedora repos rpm." + continue + fi + + echo "Fetching fedora repos rpm from ${RELEASE_URL}/${REPOS_RPM}..." + curl -L -f "${RELEASE_URL}/${REPOS_RPM}" > ${INSTALL_ROOT}/${REPOS_RPM} + if [ $? -ne 0 ]; then + echo "Failed to download fedora repos rpm ${RELEASE_RPM}." + continue + fi + fi + + + DOWNLOAD_OK=yes + break + done + + if [ $DOWNLOAD_OK != yes ]; then + echo "Aborting" + return 1 + fi + + mkdir -p ${INSTALL_ROOT}/var/lib/rpm + + if ! fedora_get_bootstrap + then + echo "Fedora Bootstrap setup failed" + return 1 + fi + + fedora_bootstrap_mounts + + ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} --initdb + + # The --nodeps is STUPID but F15 had a bogus dependency on RawHide?!?! + ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} --nodeps -ivh ${BOOTSTRAP_INSTALL_ROOT}/${RELEASE_RPM} + + # F21 and newer need fedora-repos in addition to fedora-release... + # Note that fedora-release and fedora-system have a mutual dependency. + # So installing the reops package after the release package we can + # spare one --nodeps. + if [ "$release" -ge "21" ]; then + ${BOOTSTRAP_CHROOT}rpm --root ${BOOTSTRAP_INSTALL_ROOT} -ivh ${BOOTSTRAP_INSTALL_ROOT}/${REPOS_RPM} + fi + + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" ${BOOTSTRAP_DIR}/${BOOTSTRAP_INSTALL_ROOT}/etc/yum.repos.d/* + + ${BOOTSTRAP_CHROOT}yum --installroot ${BOOTSTRAP_INSTALL_ROOT} -y --nogpgcheck install ${PKG_LIST} + + RC=$? + + if [[ ${BOOTSTRAP} -eq 1 ]] + then + # Here we have a bit of a sticky problem. We MIGHT have just installed + # this template cache using versions of yum and rpm in the bootstrap + # chroot that use a different database version than the target version. + # That can be a very big problem. Solution is to rebuild the rpmdatabase + # with the target database now that we are done building the cache. In the + # vast majority of cases, this is a do-not-care with no harm done if we + # didn't do it. But it catches several corner cases with older unsupported + # releases and it really doesn't cost us a lot of time for a one shot + # install that will never be done again for this rev. + # + # Thanks and appreciation to Dwight Engen and the Oracle template for the + # database rewrite hint! + + echo "Fixing up rpm databases" + + # Change to our target install directory (if we're not already + # there) just to simplify some of the logic to follow... + cd ${INSTALL_ROOT} + + rm -f var/lib/rpm/__db* + # Programmers Note (warning): + # + # Pay careful attention to the following commands! It + # crosses TWO chroot boundaries linked by a bind mount! + # In the bootstrap case, that's the bind mount of ${INSTALL_ROOT} + # to the ${BOOTSTRAP_CHROOT}/run/install directory! This is + # a deliberate hack across that bind mount to do a database + # translation between two environments, neither of which may + # be the host environment! It's ugly and hard to follow but, + # if you don't understand it, don't mess with it! The pipe + # is in host space between the two chrooted environments! + # This is also why we cd'ed into the INSTALL_ROOT directory + # in advance of this loop, so everything is relative to the + # current working directory and congruent with the same working + # space in both chrooted environments. The output into the new + # db is also done in INSTALL_ROOT space but works in either host + # space or INSTALL_ROOT space for the mv, so we don't care. It's + # just not obvious what's happening in the db_dump and db_load + # commands... + # + for db in var/lib/rpm/* ; do + ${BOOTSTRAP_CHROOT} db_dump ${BOOTSTRAP_INSTALL_ROOT}/$db | chroot . db_load $db.new + mv $db.new $db + done + # finish up by rebuilding the database... + # This should be redundant but we do it for completeness and + # any corner cases I may have missed... + mount -t proc proc proc + mount -o bind /dev dev + chroot . rpm --rebuilddb + umount dev + umount proc + fi + + fedora_bootstrap_umounts + + if [ ${RC} -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + + mv "$INSTALL_ROOT" "$cache/rootfs" + echo "Download complete." + + return 0 +} + +copy_fedora() +{ + + # make a local copy of the minifedora + echo -n "Copying rootfs to $rootfs_path ..." + #cp -a $cache/rootfs-$basearch $rootfs_path || return 1 + # i prefer rsync (no reason really) + mkdir -p $rootfs_path + rsync -SHaAX $cache/rootfs/ $rootfs_path/ + echo + return 0 +} + +update_fedora() +{ + mount -o bind /dev ${cache}/rootfs/dev + mount -t proc proc ${cache}/rootfs/proc + # Always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf ${cache}/rootfs/etc/ + chroot ${cache}/rootfs yum -y update + umount ${cache}/rootfs/proc + umount ${cache}/rootfs/dev +} + +install_fedora() +{ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ + ( + flock -x 9 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + echo "Checking cache download in $cache/rootfs ... " + if [ ! -e "$cache/rootfs" ]; then + download_fedora + if [ $? -ne 0 ]; then + echo "Failed to download 'fedora base'" + return 1 + fi + else + echo "Cache found. Updating..." + update_fedora + if [ $? -ne 0 ]; then + echo "Failed to update 'fedora base', continuing with last known good cache" + else + echo "Update finished" + fi + fi + + echo "Copy $cache/rootfs to $rootfs_path ... " + copy_fedora + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + + return 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-fedora + + return $? +} + +# Generate a random hardware (MAC) address composed of FE followed by +# 5 random bytes... +create_hwaddr() +{ + openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' +} + +copy_configuration() +{ + mkdir -p $config_path + + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo " +lxc.rootfs.path = $rootfs_path +" >> $config_path/config + + # The following code is to create static MAC addresses for each + # interface in the container. This code will work for multiple + # interfaces in the default config. It will also strip any + # hwaddr stanzas out of the default config since we can not share + # MAC addresses between containers. + mv $config_path/config $config_path/config.def + while read LINE + do + # This should catch variable expansions from the default config... + if expr "${LINE}" : '.*\$' > /dev/null 2>&1 + then + LINE=$(eval "echo \"${LINE}\"") + fi + + # There is a tab and a space in the regex bracket below! + # Seems that \s doesn't work in brackets. + KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') + + if [[ "${KEY}" != "lxc.net.0.hwaddr" ]] + then + echo "${LINE}" >> $config_path/config + + if [[ "${KEY}" == "lxc.net.0.link" ]] + then + echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $config_path/config + fi + fi + done < $config_path/config.def + + rm -f $config_path/config.def + + if [ -e "@LXCTEMPLATECONFIG@/fedora.common.conf" ]; then + echo " +# Include common configuration +lxc.include = @LXCTEMPLATECONFIG@/fedora.common.conf +" >> $config_path/config + fi + + # Append things which require expansion here... + cat <> $config_path/config +lxc.arch = $arch +lxc.uts.name = $utsname + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +# example simple networking setup, uncomment to enable +#lxc.net.0.type = $lxc_network_type +#lxc.net.0.flags = up +#lxc.net.0.link = $lxc_network_link +#lxc.net.0.name = eth0 +# Additional example for veth network type +# static MAC address, +#lxc.net.0.hwaddr = 00:16:3e:77:52:20 +# persistent veth device name on host side +# Note: This may potentially collide with other containers of same name! +#lxc.net.0.veth.pair = v-$name-e0 + +EOF + + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +clean() +{ + + if [ ! -e $cache ]; then + exit 0 + fi + + # lock, so we won't purge while someone is creating a repository + ( + flock -x 9 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + + echo -n "Purging the download cache for Fedora-$release..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-fedora +} + +usage() +{ + cat < + [-p|--path=] [-c|--clean] [-R|--release=] + [--fqdn=] [-a|--arch=] + [--mask-tmp] + [-h|--help] +Mandatory args: + -n,--name container name, used to as an identifier for that container +Optional args: + -p,--path path to where the container will be created, + defaults to @LXCPATH@. + --rootfs path for actual rootfs. + -c,--clean clean the cache + -R,--release Fedora release for the new container. + Defaults to host's release if the host is Fedora. + --fqdn fully qualified domain name (FQDN) for DNS and system naming + -a,--arch Define what arch the container will be [i686,x86_64] + --mask-tmp Prevent systemd from over-mounting /tmp with tmpfs. + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o a:hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,arch:,fqdn:,mask-tmp -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi + +arch=$(uname -m) +masktmp=0 + +eval set -- "$options" +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs_path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -c|--clean) clean=1; shift 1;; + -R|--release) release=$2; shift 2;; + -a|--arch) newarch=$2; shift 2;; + --fqdn) utsname=$2; shift 2;; + --mask-tmp) masktmp=1; shift 1;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ ! -z "$clean" -a -z "$path" ]; then + clean || exit 1 + exit 0 +fi + +basearch=${arch} +# Map a few architectures to their generic Fedora repository archs. +# The two ARM archs are a bit of a guesstimate for the v5 and v6 +# archs. V6 should have hardware floating point (Rasberry Pi). +# The "arm" arch is safer (no hardware floating point). So +# there may be cases where we "get it wrong" for some v6 other +# than RPi. +case "$arch" in +i686) basearch=i386 ;; +armv3l|armv4l|armv5l) basearch=arm ;; +armv6l|armv7l|armv8l) basearch=armhfp ;; +*) ;; +esac + +mirrorurl="archives.fedoraproject.org::fedora-archive" +case "$basearch" in +ppc64|s390x) mirrorurl="archives.fedoraproject.org::fedora-secondary" ;; +*) ;; +esac + +# Somebody wants to specify an arch. This is very limited case. +# i386/i586/i686 on i386/x86_64 +# - or - +# x86_64 on x86_64 +if [ "${newarch}" != "" -a "${newarch}" != "${arch}" ] +then + case "${newarch}" in + i386|i586|i686) + if [ "${basearch}" = "i386" -o "${basearch}" = "x86_64" ] + then + # Make the arch a generic x86 32 bit... + arch=${newarch} + basearch=i386 + else + basearch=bad + fi + ;; + *) + basearch=bad + ;; + esac + + if [ "${basearch}" = "bad" ] + then + echo "You cannot build a ${newarch} Fedora container on a ${arch} host. Sorry!" + exit 1 + fi +fi + +# Allow the cache base to be set by environment variable +cache_base=${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/fedora/$basearch + +# Let's do something better for the initial root password. +# It's not perfect but it will defeat common scanning brute force +# attacks in the case where ssh is exposed. It will also be set to +# expired, forcing the user to change it at first login. +if [ "${root_password}" = "" ] +then + root_password=Root-${name}-${RANDOM} +else + # If it's got a ding in it, try and expand it! + if [ $(expr "${root_password}" : '.*$.') != 0 ] + then + root_password=$(eval echo "${root_password}") + fi + + # If it has more than 3 consecutive X's in it, feed it + # through mktemp as a template. + if [ $(expr "${root_password}" : '.*XXXX') != 0 ] + then + root_password=$(mktemp -u ${root_password}) + fi +fi + +if [ -z "${utsname}" ]; then + utsname=${name} +fi + +# This follows a standard "resolver" convention that an FQDN must have +# at least two dots or it is considered a local relative host name. +# If it doesn't, append the dns domain name of the host system. +# +# This changes one significant behavior when running +# "lxc_create -n Container_Name" without using the +# --fqdn option. +# +# Old behavior: +# utsname and hostname = Container_Name +# New behavior: +# utsname and hostname = Container_Name.Domain_Name + +if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then + if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then + utsname=${utsname}.$(dnsdomainname) + fi +fi + +needed_pkgs="" + +type curl >/dev/null 2>&1 +if [ $? -ne 0 ]; then + needed_pkgs="curl $needed_pkgs" +fi +type openssl >/dev/null 2>&1 +if [ $? -ne 0 ]; then + needed_pkgs="openssl $needed_pkgs" +fi + +if [ -n "$needed_pkgs" ]; then + echo "Missing commands: $needed_pkgs" + echo "Please install these using \"sudo yum install $needed_pkgs\"" + exit 1 +fi + +if [ -z "$path" ]; then + path=$default_path/$name +fi + +if [ -z "$release" ]; then + if [ "$is_fedora" -a "$fedora_host_ver" ]; then + release=$fedora_host_ver + else + echo "This is not a fedora host and release missing, defaulting to 22 use -R|--release to specify release" + release=22 + fi +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -z "$rootfs_path" ]; then + rootfs_path=$path/rootfs + # check for 'lxc.rootfs.path' passed in through default config by lxc-create + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(sed -e '/^lxc.rootfs.path\s*=/!d' -e 's/\s*#.*//' \ + -e 's/^lxc.rootfs.path\s*=\s*//' -e q $path/config) + fi +fi +config_path=$path +cache=$cache_base/$release + +revert() +{ + echo "Interrupted, so cleaning up" + lxc-destroy -n $name + # maybe was interrupted before copy config + rm -rf $path + echo "exiting..." + exit 1 +} + +trap revert SIGHUP SIGINT SIGTERM + +copy_configuration +if [ $? -ne 0 ]; then + echo "failed write configuration file" + exit 1 +fi + +install_fedora +if [ $? -ne 0 ]; then + echo "failed to install fedora" + exit 1 +fi + +configure_fedora +if [ $? -ne 0 ]; then + echo "failed to configure fedora for a container" + exit 1 +fi + +# If the systemd configuration directory exists - set it up for what we need. +if [ -d ${rootfs_path}/etc/systemd/system ] +then + configure_fedora_systemd +fi + +# This configuration (rc.sysinit) is not inconsistent with the systemd stuff +# above and may actually coexist on some upgraded systems. Let's just make +# sure that, if it exists, we update this file, even if it's not used... +if [ -f ${rootfs_path}/etc/rc.sysinit ] +then + configure_fedora_init +fi + +if [ ! -z "$clean" ]; then + clean || exit 1 + exit 0 +fi +echo " +Container rootfs and config have been created. +Edit the config file to check/enable networking setup. +" + +if [[ -d ${cache_base}/bootstrap ]] +then + echo "You have successfully built a Fedora container and cache. This cache may +be used to create future containers of various revisions. The directory +${cache_base}/bootstrap contains a bootstrap +which may no longer needed and can be removed. +" +fi + +if [[ -e ${cache_base}/LiveOS ]] +then + echo "A LiveOS directory exists at ${cache_base}/LiveOS. +This is only used in the creation of the bootstrap run-time-environment +and may be removed. +" +fi + +if [ ${root_display_password} = "yes" ] +then + echo "The temporary password for root is: '$root_password' + +You may want to note that password down before starting the container. +" +fi + +if [ ${root_store_password} = "yes" ] +then + echo "The temporary root password is stored in: + + '${config_path}/tmp_root_pass' +" +fi + +if [ ${root_prompt_password} = "yes" ] +then + echo "Invoking the passwd command in the container to set the root password. + + chroot ${rootfs_path} passwd +" + chroot ${rootfs_path} passwd +else + if [ ${root_expire_password} = "yes" ] + then + if ( mountpoint -q -- "${rootfs_path}" ) + then + echo "To reset the root password, you can do: + + lxc-start -n ${name} + lxc-attach -n ${name} -- passwd + lxc-stop -n ${name} +" + else + echo " +The root password is set up as "expired" and will require it to be changed +at first login, which you should do as soon as possible. If you lose the +root password or wish to change it without starting the container, you +can change it from the host by running the following command (which will +also reset the expired flag): + + chroot ${rootfs_path} passwd +" + fi + fi +fi diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in new file mode 100644 index 0000000..10b90b1 --- /dev/null +++ b/templates/lxc-fedora.in @@ -0,0 +1,1291 @@ +#!/bin/bash + +# +# template script for generating Fedora container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# Daniel Lezcano +# Ramez Hanna +# Michael H. Warfield +# Reto Gantenbein + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# configurations +FEDORA_RELEASE_MIN=24 +FEDORA_RELEASE_DEFAULT=${FEDORA_RELEASE_DEFAULT:-25} +FEDORA_RSYNC_URL="${FEDORA_RSYNC_URL:-archives.fedoraproject.org::fedora-enchilada}" +MIRRORLIST_URL=${MIRRORLIST_URL:-https://mirrors.fedoraproject.org/mirrorlist} + +local_state_dir="@LOCALSTATEDIR@" +lxc_path="@LXCPATH@" +lxc_template_config="@LXCTEMPLATECONFIG@" +lxc_default_conf="@LXC_DEFAULT_CONFIG@" + +# allows the cache directory to be set by environment variable +LXC_CACHE_PATH="${LXC_CACHE_PATH:-"${local_state_dir}/cache/lxc"}" + +# these are only going into comments in the resulting config... +lxc_network_type=veth +lxc_network_link=lxcbr0 + +# Some combinations of the tuning knobs below do not exactly make sense. +# but that's ok. +# +# If the "root_password" is non-blank, use it, else set a default. +# This can be passed to the script as an environment variable and is +# set by a shell conditional assignment. Looks weird but it is what it is. +# +# If the root password contains a ding ($) then try to expand it. +# That will pick up things like ${name} and ${RANDOM}. +# If the root password contains more than 3 consecutive X's, pass it as +# a template to mktemp and take the result. +# +# If root_display_password = yes, display the temporary root password at exit. +# If root_store_password = yes, store it in the configuration directory +# If root_prompt_password = yes, invoke "passwd" to force the user to change +# the root password after the container is created. +# If root_expire_password = yes, you will be prompted to change the root +# password at the first login. +# +# These are conditional assignments... The can be overridden from the +# preexisting environment variables... +# +# Make sure this is in single quotes to defer expansion to later! +# :{root_password='Root-${name}-${RANDOM}'} +: "${root_password='Root-${name}-XXXXXX'}" + +# Now, it doesn't make much sense to display, store, and force change +# together. But, we gotta test, right??? +: "${root_display_password='no'}" +: "${root_store_password='yes'}" +# Prompting for something interactive has potential for mayhem +# with users running under the API... Don't default to "yes" +: "${root_prompt_password='no'}" + +# Expire root password? Default to yes, but can be overridden from +# the environment variable +: "${root_expire_password='yes'}" + +# detect use under userns (unsupported) +for arg in "$@"; do + [ "${arg}" = "--" ] && break + if [ "${arg}" = "--mapped-uid" ] || [ "${arg}" = "--mapped-gid" ] + then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# make sure the usual locations are in PATH +export PATH=${PATH}:/usr/sbin:/usr/bin:/sbin:/bin + +# dnf package manager arguments +dnf_args=( --assumeyes --best --allowerasing --disablerepo=* --enablerepo=fedora --enablerepo=updates ) + +# This function is going to setup a minimal host-arch native Fedora bootstrap +# environment which will be used to create new containers on non-Fedora hosts. +# +bootstrap_fedora() +{ + local cache="${1}" + + local bootstrap="${cache}/bootstrap" + if [ -d "${bootstrap}" ] + then + echo "Existing Fedora bootstrap environment found. Testing ..." + if chroot_update_fedora "${bootstrap}" + then + CHROOT_DIR="${bootstrap}" + CHROOT_CMD="chroot ${CHROOT_DIR} " + + echo "Bootstrap environment appears to be functional." + return 0 + else + echo "Error: Bootstrap environment detected in ${bootstrap}" + echo "but appears to be non-functional. Please remove and restart." + return 1 + fi + fi + + echo "Setting up new Fedora ${FEDORA_RELEASE_DEFAULT} (${basearch}) bootstrap environment." + + [[ -d "${cache}" ]] || mkdir -p "${cache}" + + tmp_bootstrap_dir=$( mktemp -d --tmpdir="${cache}" bootstrap_XXXXXX ) + pushd "${tmp_bootstrap_dir}" >/dev/null + + mkdir squashfs liveos bootstrap + + # download the LiveOS squashfs image + if [ ! -f "${cache}/install.img" ] + then + local os_path="linux/releases/${FEDORA_RELEASE_DEFAULT}/Everything/${basearch}/os" + local image_path="images/install.img" + local ret=1 + + if [ -n "${rsync}" ] + then + echo "Syncing LiveOS squashfs image from ${FEDORA_RSYNC_URL} ... " + rsync --archive --info=progress "${FEDORA_RSYNC_URL}/${os_path}/${image_path}" . + ret=$? + else + if [ -z "${mirror}" ] + then + get_mirrors "${FEDORA_RELEASE_DEFAULT}" "${basearch}" || return $? + else + local mirror_url="${mirror}/${os_path}" + fi + for url in ${mirror:-${mirror_urls}} + do + echo "Downloading LiveOS squashfs image from ${url} ... " + curl --silent --show-error --fail --remote-name "${url}/${image_path}" + ret=$? + if [ ${ret} -ne 0 ] + then + continue + else + break + fi + done + fi + + if [ "${ret}" != 0 ] || [ ! -s install.img ] + then + echo "Error: Download of squashfs image failed." + return 1 + fi + mv install.img "${cache}/" + else + echo "Using cached LiveOS squashfs image." + fi + + echo "Mounting LiveOS squashfs file system." + if ! mount -o loop -t squashfs "${cache}/install.img" squashfs/ + then + echo " +Error: Mount of LiveOS squashfs image failed +-------------------------------------------- +You must have squashfs support available to mount image. LiveOS image is now +cached. Process may be rerun without penalty of downloading LiveOS again. If +LiveOS is corrupt, remove ${cache}/install.img +" + return 1 + fi + + # mount contained LiveOS + if ! mount -o loop squashfs/LiveOS/rootfs.img liveos + then + echo " +Error: Mount of LiveOS stage0 rootfs image failed +------------------------------------------------- +LiveOS download may be corrupt. Remove ${cache}/LiveOS +to force a new download. +" + return 1 + fi + + echo "Copying LiveOS content to bootstrap environment ... " + if ! rsync --archive --acls --hard-links --sparse liveos/. bootstrap/ + then + echo "Error: Build of bootstrap environment failed." + echo "Keeping directory ${tmp_bootstrap_dir} for your investigation." + exit 1 + fi + + # unmount liveos mounts - we're done with liveos at this point + umount liveos + umount squashfs + + # customize bootstrap rootfs + pushd bootstrap >/dev/null + + # setup repositories in bootstrap chroot + CHROOT_DIR="$(pwd)" + CHROOT_CMD="chroot ${CHROOT_DIR} " + INSTALL_ROOT="/" + if ! setup_repositories "${cache}" "${basearch}" "${FEDORA_RELEASE_DEFAULT}" "${mirror}" + then + echo "Error: Failed to configure repositories in ${CHROOT_DIR}${INSTALL_ROOT}" + exit 1 + fi + if [ -n "${mirror}" ] + then + set_dnf_mirror_url ./etc/yum.repos.d/fedora*.repo + fi + + # make sure /etc/resolv.conf is up to date in the chroot + cp /etc/resolv.conf ./etc/ + + # verify bootstrap chroot by running a dnf update + chroot_update_fedora "$(pwd)" + ret=$? + + popd >/dev/null + + if [ "${ret}" != 0 ] + then + echo "Error: Build of bootstrap environment failed." + echo "Keeping directory ${tmp_bootstrap_dir} for your investigation." + return 1 + fi + + mv bootstrap "${cache}" + + popd >/dev/null + rm -rf "${tmp_bootstrap_dir}" + + CHROOT_DIR="${bootstrap}" + CHROOT_CMD="chroot ${CHROOT_DIR} " + + echo "Fedora bootstrap environment successfully created." + return 0 +} + +chroot_mounts() +{ + test -n "${1}" && local chroot="${1}" || return 1 + + mount -t proc proc "${chroot}/proc" && + mount -o bind /dev "${chroot}/dev" +} + +chroot_umounts() +{ + test -n "${1}" && local chroot="${1}" || return 1 + + umount "${chroot}/proc" && + umount "${chroot}/dev" +} + +clean_cache() +{ + local cache="${1}" + + test ! -e "${cache}" && return 0 + + # lock, so we won't purge while someone is creating a repository + ( + if ! flock -x 9 + then + echo "Error: Cache repository is busy." + exit 1 + fi + + echo -n "Purging the Fedora bootstrap and download cache ... " + rm --preserve-root --one-file-system -rf "${cache}" && echo "Done." || exit 1 + + exit 0 + + ) 9>"${local_state_dir}/lock/subsys/lxc-fedora" + + return $? +} + +# Customize container rootfs +# +configure_fedora() +{ + local rootfs="${1}" + local release="${2}" + local utsname="${3}" + + # disable selinux + mkdir -p "${rootfs}/selinux" + echo 0 > "${rootfs}/selinux/enforce" + + # also kill it in the /etc/selinux/config file if it's there... + if [ -f "${rootfs}/etc/selinux/config" ] + then + sed -i '/^SELINUX=/s/.*/SELINUX=disabled/' "${rootfs}/etc/selinux/config" + fi + + # nice catch from Dwight Engen in the Oracle template. + # wantonly plagerized here with much appreciation. + if [ -f "${rootfs}/usr/sbin/selinuxenabled" ] + then + rm -f "${rootfs}/usr/sbin/selinuxenabled" + ln -s /bin/false "${rootfs}/usr/sbin/selinuxenabled" + fi + + # set hostname + echo "${utsname}" > "${rootfs}/etc/hostname" + + # set default localtime to the host localtime if not set... + if [ -e /etc/localtime ] && [ ! -e "${rootfs}/etc/localtime" ] + then + # if /etc/localtime is a symlink, this should preserve it. + cp -a /etc/localtime "${rootfs}/etc/localtime" + fi + + # set minimal hosts + cat < "${rootfs}/etc/hosts" +127.0.0.1 localhost.localdomain localhost ${utsname} +::1 localhost6.localdomain6 localhost6 +EOF + + # setup console and tty[1-4] for login. note that /dev/console and + # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and + # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. + # lxc will maintain these links and bind mount ptys over /dev/lxc/* + # since lxc.tty.dir is specified in the config. + + # allow root login on console, tty[1-4], and pts/0 for libvirt + cat <> "${rootfs}/etc/securetty" +# LXC (Linux Containers) +lxc/console +lxc/tty1 +lxc/tty2 +lxc/tty3 +lxc/tty4 +# For libvirt/Virtual Machine Monitor +pts/0 +EOF + + if [ "${root_display_password}" = yes ] + then + echo "Setting root password to '$root_password'" + fi + if [ "${root_store_password}" = yes ] + then + touch "${path}/tmp_root_pass" + chmod 600 "${path}/tmp_root_pass" + echo "${root_password}" > "${path}/tmp_root_pass" + echo "Storing root password in '${path}/tmp_root_pass'" + fi + + echo "root:$root_password" | chroot "${rootfs}" chpasswd + + if [ "${root_expire_password}" = yes ] + then + # also set this password as expired to force the user to change it! + chroot "${rootfs}" passwd -e root + fi + + chroot_mounts "${rootfs}" + + # always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf "${rootfs}/etc/" + + # rebuild the rpm database based on the target rpm version... + rm -f "${rootfs}"/var/lib/rpm/__db* + chroot "${rootfs}" rpm --rebuilddb + + chroot_umounts "${rootfs}" + + # default systemd target + chroot "${rootfs}" ln -s /lib/systemd/system/multi-user.target \ + /etc/systemd/system/default.target + + # enable networking via systemd-networkd + test -d "${rootfs}/etc/systemd/network" || mkdir "${rootfs}/etc/systemd/network" + cat < "${rootfs}/etc/systemd/network/eth0.network" +[Match] +Name=eth0 + +[Network] +DHCP=both +EOF + mkdir -p "${rootfs}/etc/systemd/system/socket.target.wants" + chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd.socket \ + /etc/systemd/system/socket.target.wants/systemd-networkd.socket + chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd.service \ + /etc/systemd/system/multi-user.target.wants/systemd-networkd.service + mkdir -p "${rootfs}/etc/systemd/system/network-online.target.wants" + chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-networkd-wait-online.service \ + /etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service + + # disable traditional network init + chroot "${rootfs}" chkconfig network off + + # enable systemd-resolved + rm -f "${rootfs}/etc/resolv.conf" + chroot "${rootfs}" ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf + chroot "${rootfs}" ln -s /usr/lib/systemd/system/systemd-resolved.service \ + /etc/systemd/system/multi-user.target.wants/systemd-resolved.service + + # if desired, prevent systemd from over-mounting /tmp with tmpfs + if [ "${masktmp}" -eq 1 ] + then + chroot "${rootfs}" ln -s /dev/null /etc/systemd/system/tmp.mount + fi + + return 0 +} + +copy_configuration() +{ + local rootfs="${1}" + local config="${2}" + local utsname="${3}" + + # include configuration from default.conf if available + grep -q "^lxc." "${lxc_default_conf}" > "${config}" 2>/dev/null + + grep -q "^lxc.rootfs.path" "${config}" 2>/dev/null || echo " +lxc.rootfs.path = ${rootfs} +" >> "${config}" + + # The following code is to create static MAC addresses for each + # interface in the container. This code will work for multiple + # interfaces in the default config. It will also strip any + # hwaddr stanzas out of the default config since we can not share + # MAC addresses between containers. + mv "${config}" "${config}.orig" + + local line key + while read -r line + do + # This should catch variable expansions from the default config... + if expr "${line}" : '.*\$' > /dev/null 2>&1 + then + line=$(eval "echo \"${line}\"") + fi + + # There is a tab and a space in the regex bracket below! + # Seems that \s doesn't work in brackets. + key=$(expr "${line}" : '\s*\([^ ]*\)\s*=') + + if [ "${key}" != "lxc.net.0.hwaddr" ] + then + echo "${line}" >> "${config}" + + if [ "${key}" == "lxc.net.0.link" ] + then + echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> "${config}" + fi + fi + done < "${config}.orig" + rm -f "${config}.orig" + + if [ -e "${lxc_template_config}/fedora.common.conf" ] + then + echo " +# Include common configuration +lxc.include = ${lxc_template_config}/fedora.common.conf +" >> "${config}" + fi + + cat <> "${path}/config" +# Container specific configuration +lxc.arch = ${basearch} +lxc.uts.name = ${utsname} + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +# example simple networking setup, uncomment to enable +#lxc.net.0.type = ${lxc_network_type} +#lxc.net.0.flags = up +#lxc.net.0.link = ${lxc_network_link} +#lxc.net.0.name = eth0 +# Additional example for veth network type +# static MAC address, +#lxc.net.0.hwaddr = $(create_hwaddr) +# persistent veth device name on host side +# Note: This may potentially collide with other containers of same name! +#lxc.net.0.veth.pair = v-${name}-e0 +EOF + + if [ $? -ne 0 ] + then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +copy_fedora() +{ + local cache="${1}" + local rootfs="${2}" + echo -n "Copying ${cache} to ${rootfs} ... " + + mkdir -p "${rootfs}" && + rsync --archive --hard-links --sparse --acls --xattrs "${cache}/" "${rootfs}/" && + echo || return 1 + + return 0 +} + +# Generate a random hardware (MAC) address composed of FE followed by +# 5 random bytes... +# +create_hwaddr() +{ + openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' +} + +# Make sure a fully functional rootfs of the requested release and architecture +# will be setup in the given cache directory. If this is a Fedora host the +# commands will run natively otherwise in a minimal Fedora bootstrap chroot. +# +download_fedora() +{ + local cache_rootfs="${1}" + local setup_rootfs="${cache_rootfs%%/rootfs}/partial" + + # suppress errors due to unknown locales + LC_ALL=C + LANG=en_US + + echo "Downloading ${basearch} rootfs for Fedora ${release} ..." + + # The following variables are going to be overwritten if the rootfs setup + # is run in a separate boostrap environment (can not build natively). + # These are the defaults for the non-boostrap (native) mode. + CHROOT_DIR= + CHROOT_CMD= + INSTALL_ROOT=${setup_rootfs} + + if [ ! "${is_fedora}" ] || [ "${fedora_host_ver}" -lt "${FEDORA_VERSION_MINIMAL}" ] + then + # if this is not a supported Fedora host, use minimal bootstrap chroot + echo "Non-Fedora host detected. Checking for bootstrap environment ... " + if ! bootstrap_fedora "${cache}" + then + echo "Error: Fedora Bootstrap setup failed" + return 1 + fi + echo "Using bootstrap environment at ${CHROOT_DIR}" + fi + + if ! mkdir -p "${setup_rootfs}" + then + echo "Error: Failed to create '${setup_rootfs}' directory." + return 1 + fi + + trap revert_rootfs SIGHUP SIGINT SIGTERM + + mkdir -p "${setup_rootfs}/var/lib/rpm" + + # if the setup is going to be run in a chroot, mount the related file systems + if [ -n "${CHROOT_DIR}" ] + then + chroot_mounts "${CHROOT_DIR}" + + # make sure rootfs is available in bootstrap chroot + INSTALL_ROOT="/run/install" + test -d "${CHROOT_DIR}${INSTALL_ROOT}" || mkdir -p "${CHROOT_DIR}${INSTALL_ROOT}" + mount -o bind "${setup_rootfs}" "${CHROOT_DIR}${INSTALL_ROOT}" + fi + + if ! setup_repositories "${cache}" "${basearch}" "${release}" "${mirror}" + then + echo "Error: Failed to configure repositories in ${CHROOT_DIR}${INSTALL_ROOT}" + revert_rootfs >/dev/null + return 1 + fi + + # Unforunately "${CHROOT_DIR}${INSTALL_ROOT}/dnf.conf" + if [ -n "${mirror}" ] + then + set_dnf_mirror_url "${CHROOT_DIR}${INSTALL_ROOT}/dnf.conf" + fi + + # install minimal container file system + local pkg_list="dnf initscripts passwd vim-minimal openssh-server openssh-clients dhclient rootfiles policycoreutils fedora-release fedora-repos" + if ! ${CHROOT_CMD}dnf --installroot "${INSTALL_ROOT}" \ + --config="${INSTALL_ROOT}/dnf.conf" \ + --releasever "${release}" \ + ${dnf_args[@]} \ + install ${pkg_list} + then + echo "Error: Failed to setup the rootfs in ${CHROOT_DIR}${INSTALL_ROOT}." + revert_rootfs >/dev/null + return 1 + fi + + unmount_installroot + + # from now on we'll work in the new rootfs + chroot_mounts "${setup_rootfs}" + + # It might happen, that the dnf used above will write an incompatible + # RPM database for the version running in the rootfs. Rebuild it. + echo "Fixing up RPM databases" + rm -f "${setup_rootfs}"/var/lib/rpm/__db* + chroot "${setup_rootfs}" rpm --rebuilddb + + # Restrict locale for installed packages to en_US to shrink image size + # following: https://pagure.io/fedora-kickstarts/blob/master/f/fedora-cloud-base.ks + echo "Cleanup locales and language files ..." + find "${setup_rootfs}/usr/share/locale" -mindepth 1 -maxdepth 1 -type d \ + -not -name "${LANG}" -exec rm -rf {} + + + chroot "${setup_rootfs}" localedef --list-archive | grep -v ^"${LANG}" | xargs \ + chroot "${setup_rootfs}" localedef --delete-from-archive + + mv -f "${setup_rootfs}/usr/lib/locale/locale-archive" \ + "${setup_rootfs}/usr/lib/locale/locale-archive.tmpl" + chroot "${setup_rootfs}" build-locale-archive + + echo "%_install_langs C:en:${LANG}:${LANG}.UTF-8" > "${setup_rootfs}/etc/rpm/macros.image-language-conf" + + chroot_umounts "${setup_rootfs}" + + # reset traps + trap SIGHUP + trap SIGINT + trap SIGTERM + + # use generated rootfs as future cache + mv "${setup_rootfs}" "${cache_rootfs}" + + echo "Download of Fedora rootfs complete." + return 0 +} + +# Query the Fedora mirrorlist for several HTTPS mirrors +# +get_mirrors() +{ + local release="${1}" + local mirror_arch="${2}" + + for trynumber in 1 2 3 4 + do + [ "${trynumber}" != 1 ] && echo -n "Trying again ... " + + # choose some mirrors by parsing directory index + mirror_urls=$(curl --silent --show-error --fail "${MIRRORLIST_URL}?repo=fedora-${release}&arch=${mirror_arch}" | sed '/^https:/!d' | sed '2,6!d') + + # shellcheck disable=SC2181 + if [ $? -eq 0 ] && [ -n "${mirror_urls}" ] + then + break + fi + + echo "Warning: Failed to get a mirror on try ${trynumber}." + sleep 3 + done + + if [ -z "${mirror_urls}" ] + then + echo "Error: Failed to retrieve Fedora mirror URL. Please use '-m MIRROR' option." + return 1 + fi + + return 0 +} + +# Install a functional Fedora rootfs into the container root +# +install_fedora() +{ + local rootfs="${1}" + local cache="${2}" + local cache_rootfs="${cache}/${release}-${basearch}/rootfs" + + mkdir -p "${local_state_dir}/lock/subsys/" + ( + if ! flock -x 9 + then + echo "Error: Cache repository is busy." + return 1 + fi + + echo "Checking cache download in ${cache_rootfs} ... " + if [ ! -e "${cache_rootfs}" ] + then + if ! download_fedora "${cache_rootfs}" + then + echo "Error: Failed to download Fedora ${release} (${basearch})" + return 1 + fi + else + echo "Cache found at ${cache_rootfs}. Updating ..." + if ! chroot_update_fedora "${cache_rootfs}" + then + echo "Failed to update cached rootfs, continuing with previously cached version." + else + echo "Fedora update finished." + fi + fi + + trap revert_container SIGHUP SIGINT SIGTERM + + if ! copy_fedora "${cache_rootfs}" "${rootfs}" + then + echo "Error: Failed to copy rootfs" + return 1 + fi + + chroot_mounts "${rootfs}" + + # install additional user provided packages + if [ -n "${packages}" ] + then + # always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf "${rootfs}/etc/" + + echo "Installing user requested RPMs: ${packages}" + if ! chroot "${rootfs}" dnf install ${dnf_args[@]} $(tr ',' ' ' <<< "${packages}") + then + echo "Error: Installation of user provided packages failed." + echo "Cleaning up ... " + sleep 3 # wait for all file handles to properly close + chroot_umounts "${setup_rootfs}" + return 1 + fi + fi + + # cleanup dnf cache in new container + chroot "${rootfs}" dnf clean all + + sleep 3 # wait for all file handles to properly close + chroot_umounts "${rootfs}" + + return 0 + ) 9>"${local_state_dir}/lock/subsys/lxc-fedora" + + return $? +} + +# Cleanup partial container +# +revert_container() +{ + echo "Interrupted, so cleaning up ..." + lxc-destroy -n "${name}" 2>/dev/null + # maybe was interrupted before copy config, try to prevent some mistakes + if [ -d "${path}" ] && + [ "${path}" != "/" ] && [ "${path}" != "/tmp" ] && [ "${path}" != "/bin" ] + then + rm -rf "${path}" + fi + echo "Exiting ..." + exit 1 +} + +# Cleanup partial rootfs cache +# +revert_rootfs() +{ + echo "Interrupted, so cleaning up ..." + unmount_installroot + rm -rf "${setup_rootfs}" + echo "Exiting ..." + exit 1 +} + +# Set dnf repository mirror in given repo files +# +set_dnf_mirror_url() +{ + sed -i -e 's/^\(metalink=.*\)$/#\1/g' "${@}" + sed -i -e '/baseurl/ s|^#||g' "${@}" + sed -i -e "/baseurl/ s|http://download.fedoraproject.org/pub/fedora|${mirror}|g" "${@}" +} + +# Setup dnf repository configuration. It can be run in a chroot by specifying +# $CHROOT_DIR (chroot directory) and $CHROOT_CMD (chroot command) and/or +# with an alternative RPM install root defined in $INSTALL_ROOT. +# +setup_repositories() +{ + local cache="${1}" + local target_arch="${2}" + local release="${3}" + local mirror="${4}" + + # download repository packages if not found in cache + pushd "${cache}" >/dev/null + if [ -z "$(ls -1 ./fedora-release-${release}*.noarch.rpm 2>/dev/null)" ] || + [ -z "$(ls -1 ./fedora-repos-${release}*.noarch.rpm 2>/dev/null)" ] + then + # if no mirror given, get an appropriate mirror from the mirror list + if [ -z "${mirror}" ] + then + get_mirrors "${release}" "${target_arch}" || return $? + else + # construct release-specific mirror url + mirror="${mirror}/linux/releases/${release}/Everything/${target_arch}/os" + fi + + for mirror_url in ${mirror:-${mirror_urls}} + do + local release_url="${mirror_url}/Packages/f" + + for pkg in fedora-release-${release} fedora-repos-${release} + do + test -n "$(ls -1 ./${pkg}*.noarch.rpm 2>/dev/null)" && continue + + # query package name by parsing directory index + echo "Requesting '${pkg}' package version from ${release_url}." + pkg_name=$(curl --silent --show-error --fail "${release_url}/" | sed -n -e "/${pkg}/ s/.*href=\"\(${pkg}-.*\.noarch\.rpm\)\">.*/\1/p" | tail -1) + + # shellcheck disable=SC2181 + if [ $? -ne 0 ] || [ -z "${pkg_name}" ] + then + echo "Error: Failed to get '${pkg}' version from ${release_url}/." + continue + fi + + echo "Downloading '${release_url}/${pkg_name} ... " + if ! curl --silent --show-error --fail --remote-name "${release_url}/${pkg_name}" + then + echo "Error: Package download failed." + continue + fi + done + + # if we have both packages continue + if [ -z "$(ls -1 ./fedora-release-${release}*.noarch.rpm 2>/dev/null)" ] || + [ -z "$(ls -1 ./fedora-repos-${release}*.noarch.rpm 2>/dev/null)" ] + then + break + fi + done + fi + + # copy packages to chroot file system + if [ -n "${CHROOT_DIR}" ] + then + cp ./fedora-release-${release}*.noarch.rpm "${CHROOT_DIR}" && + cp ./fedora-repos-${release}*.noarch.rpm "${CHROOT_DIR}" + else + local pkgdir="${cache}" + fi + + # use '--nodeps' to work around 'fedora-release-24-*' bash dependency + ${CHROOT_CMD}rpm --root "${INSTALL_ROOT}" -ivh --nodeps "${pkgdir}"/{fedora-release-${release}*.noarch.rpm,fedora-repos-${release}*.noarch.rpm} + local ret=$? + + # dnf will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|${target_arch}|" ${CHROOT_DIR}${INSTALL_ROOT}/etc/yum.repos.d/* + + popd >/dev/null + + if [ "${ret}" -ne 0 ] + then + echo "Failed to setup repositories in ${CHROOT_DIR}${INSTALL_ROOT}" + exit 1 + fi + + # cleanup installed packages + if [ -n "${CHROOT_DIR}" ] + then + # shellcheck disable=SC2086 + rm -f "${CHROOT_DIR}"/{fedora-release-${release}*.noarch.rpm,fedora-repos-${release}*.noarch.rpm} + fi + + return 0 +} + +# Run dnf update in the given chroot directory. +# +chroot_update_fedora() +{ + local chroot="${1}" + chroot_mounts "${chroot}" + + # always make sure /etc/resolv.conf is up to date in the target! + cp /etc/resolv.conf "${chroot}/etc/" + + chroot "${chroot}" dnf -y update + local ret=$? + + sleep 3 # wait for all file handles to properly close + + chroot_umounts "${chroot}" + + return ${ret} +} + +# Unmount installroot after bootstrapping or on error. +# +unmount_installroot() { + if [ -n "${CHROOT_DIR}" ] + then + sleep 3 # wait for all file handles to properly close + chroot_umounts "${CHROOT_DIR}" 2>/dev/null + umount "${CHROOT_DIR}${INSTALL_ROOT}" 2>/dev/null + fi +} + +usage() +{ + cat </dev/null 2>&1 + then + needed_pkgs="${pkg} ${needed_pkgs}" + fi +done +if [ -n "${needed_pkgs}" ] +then + echo "Error: Missing command(s): ${needed_pkgs}" + exit 1 +fi + +if [ "$(id -u)" != "0" ] +then + echo "This script should be run as 'root'" + exit 1 +fi + +# cleanup cache if requested +cache="${LXC_CACHE_PATH}/fedora" +if [ -n "${clean}" ] +then + clean_cache "${cache}" || exit 1 + exit 0 +fi + +# set container directory +if [ -z "${path}" ] +then + path="${lxc_path}/${name}" +fi + +# set container rootfs and configuration path +config="${path}/config" +if [ -z "${rootfs}" ] +then + # check for 'lxc.rootfs.path' passed in through default config by lxc-create + if grep -q '^lxc.rootfs.path' "${config}" 2>/dev/null + then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' "${config}") + else + rootfs="${path}/rootfs" + fi +fi + +# set release if not given +if [ -z "${release}" ] +then + if [ "${is_fedora}" ] && [ -n "${fedora_host_ver}" ] + then + echo "Detected Fedora ${fedora_host_ver} host. Set release to ${fedora_host_ver}." + release="${fedora_host_ver}" + else + echo "This is not a Fedora host or release is missing, defaulting release to ${FEDORA_RELEASE_DEFAULT}." + release="${FEDORA_RELEASE_DEFAULT}" + fi +fi +if [ "${release}" -lt "${FEDORA_RELEASE_MIN}" ] +then + echo "Error: Fedora release ${release} is not supported. Set -R at least to ${FEDORA_RELEASE_MIN}." + exit 1 +fi + +# bootstrap rootfs and copy to container file system +if ! install_fedora "${rootfs}" "${cache}" +then + echo "Error: Failed to create Fedora container" + exit 1 +fi + +# customize container file system +if ! configure_fedora "${rootfs}" "${release}" "${utsname}" +then + echo "Error: Failed to configure Fedora container" + exit 1 +fi + +# create container configuration (will be overwritten by newer lxc-create) +if ! copy_configuration "${rootfs}" "${config}" "${utsname}" +then + echo "Error: Failed write container configuration file" + exit 1 +fi + +if [ -n "${clean}" ]; then + clean || exit 1 + exit 0 +fi + +echo "Successfully created container '${name}'" + +if [ "${root_display_password}" = "yes" ] +then + echo "The temporary password for root is: '$root_password' + +You may want to note that password down before starting the container. +" +fi + +if [ "${root_store_password}" = "yes" ] +then + echo "The temporary root password is stored in: + + '${config}/tmp_root_pass' +" +fi + +if [ "${root_prompt_password}" = "yes" ] +then + echo "Invoking the passwd command in the container to set the root password. + + chroot ${rootfs} passwd +" + chroot "${rootfs}" passwd +else + if [ "${root_expire_password}" = "yes" ] + then + if ( mountpoint -q -- "${rootfs}" ) + then + echo "To reset the root password, you can do: + + lxc-start -n ${name} + lxc-attach -n ${name} -- passwd + lxc-stop -n ${name} +" + else + echo " +The root password is set up as expired and will require it to be changed +at first login, which you should do as soon as possible. If you lose the +root password or wish to change it without starting the container, you +can change it from the host by running the following command (which will +also reset the expired flag): + + chroot ${rootfs} passwd +" + fi + fi +fi + +# vim: set ts=4 sw=4 expandtab: diff --git a/templates/lxc-gentoo.in b/templates/lxc-gentoo.in new file mode 100644 index 0000000..950f17c --- /dev/null +++ b/templates/lxc-gentoo.in @@ -0,0 +1,833 @@ +#!/bin/bash + +# +# LXC template for gentoo +# +# Author: Guillaume Zitta +# +# Widely inspired from lxc-gentoo script at https://github.com/globalcitizen/lxc-gentoo +# +# this version is reworked with : +# - out of the lxc-create compat +# - vanilla gentoo config +# - ready to use cache +# + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +# Ensure strict root's umask doesen't render the VM unusable +umask 022 + +LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" + +################################################################################ +# Various helper functions +################################################################################ + +# param: $1: the name of the lock +# param: $2: the timeout for the lock +# The rest contain the command to execute and its parameters +execute_exclusively() +{ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ + + local lock_name="$1" + local timeout="$2" + shift 2 + + { + printf "Attempting to obtain an exclusive lock (timeout: %s sec) named \"%s\"...\n" "${timeout}" "$lock_name" + + flock -x -w "${timeout}" 50 + + if [[ $? -ne 0 ]]; then + printf " => unable to obtain lock, aborting.\n" + return 2 + else + printf " => done.\n" + fi + + printf " => Executing \"%s\"\n" "$*" + "$@" + retval=$? + } 50> "@LOCALSTATEDIR@/lock/subsys/lxc-gentoo-${lock_name}" + return $retval +} + +# a die function is always a good idea +die() +{ + printf "\n[the last exit code leading to this death was: %s ]\n" "$?" + local retval="$1" + shift 1 + printf "$@" + exit "$retval" +} + +# gentoo arch/variant detection +set_default_arch() +{ + printf "### set_default_arch: default arch/variant autodetect...\n" + arch=$(uname -m) + if [[ $arch =~ i.86 ]]; then + arch="x86" + variant="x86" + elif [[ $arch == "x86_64" ]]; then + arch="amd64" + variant="amd64" + elif [[ $arch =~ arm.* ]]; then + arch="arm" + variant="armv7a" + else + #who knows, it may work... + printf " => warn: unexpected arch:${arch} let me knows if it works :)\n" + variant="${arch}" + fi + printf " => Got: arch=%s variant=%s\n" "${arch}" "${variant}" +} + +store_user_message() +{ + user_message="${user_message}=> $@\n" +} + +################################################################################ +# CACHE Preparation +################################################################################ +# during setup cachedir is $cacheroot/partial-$arch-$variant +# at the end, it will be $cacheroot/rootfs-$arch-$variant + +cache_setup(){ + partialfs="${cacheroot}/partial-${arch}-${variant}" + + #if cache exists and flush not needed, return + [[ -d "${cachefs}" && -z "${flush_cache}" ]] && return 0 + + printf "###### cache_setup(): doing cache preparation\n" + local retval=1 + + #clean from failed previous run + rm -rf "${partialfs}" + mkdir -p "${partialfs}" + + #let's go + cache_precheck && \ + cache_stage3 && \ + cache_portage && \ + cache_inittab && \ + cache_net && \ + cache_dev && \ + cache_openrc && \ + cache_locale && \ + rm -rf "${cachefs}" && \ + mv "${partialfs}" "${cachefs}" && \ + printf "###### cache_setup: Cache should be ready\n" + + return $? +} + +cache_precheck() +{ + printf "### cache_precheck(): doing some pre-start checks ...\n" + # never hurts to have a fail-safe. + [[ -n "${cacheroot//\/}" ]] \ + || die 8 "\$cacheroot (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPARATORS, THIS IS *VERY* BAD!\n" "${cacheroot}" +} + +#get latest stage3 tarball +cache_stage3() +{ + printf "### cache_stage3(): stage3 cache deployment...\n" + + if [ -z "${tarball}" ]; then + #variables init + local stage3_baseurl="${mirror}/releases/${arch}/autobuilds" + + # get latest-stage3....txt file for subpath + local stage3_pointer="${stage3_baseurl}/latest-stage3-${variant}.txt" + + printf "Determining path to latest Gentoo %s (%s) stage3 archive...\n" "${arch}" "${variant}" + printf " => downloading and processing %s\n" "${stage3_pointer}" + + local stage3_latest_tarball=$(wget -q -O - "${stage3_pointer}" | tail -n1 | cut -d' ' -f1) \ + || die 6 "Error: unable to fetch\n" + + printf " => Got: %s\n" "${stage3_latest_tarball}" + + printf "Downloading/untarring the actual stage3 tarball...\n" + + compressor="j" + if echo ${stage3_latest_tarball} | grep ".xz$"; then + compressor="J" + fi + + wget -O - "${stage3_baseurl}/${stage3_latest_tarball}" \ + | tar -x${compressor}pf - --numeric-owner -C "${partialfs}" \ + || die 6 "Error: unable to fetch or untar\n" + printf " => extracted to: %s\n" "${partialfs}" + else + printf "Extracting the stage3 tarball...\n" + tar -xpf "${tarball}" --numeric-owner -C "${partialfs}" \ + || die 6 "unable to untar ${tarball} to ${partialfs}" + fi + + #check if it chroots + printf "chroot test..." + chroot ${partialfs} /bin/true || die 1 "Error: chroot %s /bin/true, failed" "${partialfs}" + printf " OK\n" + printf " => stage3 cache extracted in : %s\n" "${partialfs}" + return 0 +} + +cache_portage() +{ + printf "### cache_portage: caching portage tree tarball...\n" + [[ -z "${flush_cache}" && -f "${portage_cache}" ]] && return 0 + + rm -f ${portage_cache} + + printf "Downloading Gentoo portage (software build database) snapshot...\n" + execute_exclusively portage 60 wget -O "${portage_cache}" "${mirror}/snapshots/portage-latest.tar.bz2" \ + || die 6 "Error: unable to fetch\n" + printf " => done.\n" +} + +# custom inittab +cache_inittab() +{ + printf "### cache_inittab: tuning inittab...\n" + + INITTAB="${partialfs}/etc/inittab" + + [[ -w "$INITTAB" ]] || die 1 "Error: $INITTAB is not writeable" + + # create console + echo "# Lxc main console" >> "$INITTAB" + echo "1:12345:respawn:/sbin/agetty -a root --noclear 115200 console linux" >> "$INITTAB" + + # finally we add a pf line to enable clean shutdown on SIGPWR (issue 60) + echo "# clean container shutdown on SIGPWR" >> "$INITTAB" + echo "pf:12345:powerwait:/sbin/halt" >> "$INITTAB" + + # we also blank out /etc/issue here in order to prevent delays spawning login + # caused by attempts to determine domainname on disconnected containers + sed -i 's/[\][Oo]//g' "${partialfs}/etc/issue" +} + +cache_net() +{ + printf "### cache_net: doing some useful net tuning...\n" + # useful for chroot + # /etc/resolv.conf + grep -i 'search ' /etc/resolv.conf > "${partialfs}/etc/resolv.conf" + grep -i 'nameserver ' /etc/resolv.conf >> "${partialfs}/etc/resolv.conf" + + # fix boot-time interface config wipe under aggressive cap drop + # (openrc 0.9.8.4 ~sep 2012 - https://bugs.gentoo.org/show_bug.cgi?id=436266) + # initial warkaround was: sed -i -e 's/^#rc_nostop=""/rc_nostop="net.eth0 net.lo"/' "${partialfs}/etc/rc.conf" + # but this one does not depends on interfaces names + echo 'rc_keyword="-stop"' >> "${partialfs}/etc/conf.d/net" +} + +cache_dev() +{ + printf "### cache_dev(): /dev tuning...\n" + + #Wait for https://bugs.gentoo.org/show_bug.cgi?id=496054 + mkdir "${partialfs}/dev/pts" + mkdir "${partialfs}/dev/shm" + mkdir "${partialfs}/dev/mqueue" + + mkdir -m 755 "${partialfs}/dev/net" + mknod -m 666 "${partialfs}/dev/net/tun" c 10 200 + + return 0 +} + +# fix openrc system +cache_openrc() +{ + printf "### cache_openrc(): doing openrc tuning\n" + + #Wait for https://bugs.gentoo.org/show_bug.cgi?id=496054 + chroot "${partialfs}" sed s/-lxc//g -i "/etc/init.d/devfs" + + return 0 +} + +cache_locale() +{ + printf "### cache_locale(): initiating minimale locale en_US.UTF-8 \n" + + echo "en_US.UTF-8 UTF-8" >> "${partialfs}/etc/locale.gen" + chroot "${partialfs}" locale-gen + + return 0 +} + +################################################################################ +# CONTAINER Preparation +################################################################################ + +container_setup() { + printf "##### container_setup(): starting container setup\n" + + #in most cases lxc-create should have provided a copy of default lxc.conf + #let's tag where template starts, or just create the files + echo '### lxc-gentoo template stuff starts here' >> "$path/config" + + #Determine rootfs + #If backingstore was specified, lxc.rootfs.path should be present or --rootfs did the rootfs var creation + if [ -z "${rootfs}" ]; then + rootfs=`awk -F= '$1 ~ /^lxc.rootfs.path/ { print $2 }' "$path/config" 2>/dev/null` + if [ -z "${rootfs}" ]; then + #OK it's default + rootfs="${path}/rootfs" + fi + fi + store_user_message "rootfs of container is : ${rootfs}" + store_user_message "config of container is : ${path}/config" + + container_precheck && \ + container_rootfs && \ + container_consoles && \ + container_tz && \ + container_portage && \ + container_net && \ + container_hostname && \ + container_auth && \ + container_sshd && \ + container_conf + if [ $? -ne 0 ]; then + die 1 "container_setup(): one step didn't complete, sorry\n" + fi + + printf "###### container_setup(): container should be ready to start!\n" + printf "\n\n" + printf "You could now use you container with: lxc-start -n %s\n" "${name}" + printf "little things you should know about your container:\n" + printf "${user_message}" + return 0 +} + +container_precheck() +{ + printf "### container_precheck(): doing some pre-start checks ...\n" + # never hurts to have a fail-safe. + [[ -n "${name//\/}" ]] \ + || die 8 "\$name (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPARATORS, THIS IS *VERY* BAD!\n" "${name}" + + [[ -n "${rootfs//\/}" ]] \ + || die 8 "\$rootfs (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPARATORS, THIS IS *VERY* BAD!\n" "${rootfs}" + + [[ -n "${cachefs//\/}" ]] \ + || die 8 "\$cachefs (%s) IS EMPTY OR MADE OF ONLY DIRECTORY SEPARATORS, THIS IS *VERY* BAD!\n" "${cachefs}" + + # check if the rootfs already exists + [[ -d "${rootfs}/etc" ]] && die 18 "Error: \$rootfs (%s) already exists!" "${rootfs}" + + # check cache + [[ ! -d "${cachefs}/etc" ]] && die 1 "Error: \$cachefs (%s) not found!" "${cachefs}" + + return 0 +} + +container_rootfs() +{ + printf "#### container_rootfs(): copying rootfs %s from cache %s ...\n" "${rootfs}" "${cachefs}" + tar -c -f - --numeric-owner -C "${cachefs}" . \ + | tar -x -p -f - --numeric-owner -C "${rootfs}" \ + || die 1 "Error: cache copy to rootfs failed" + + printf "chroot test..." + chroot "${rootfs}" /bin/true || die 1 "Error: 'chroot %s /bin/true' failed" + printf " OK\n" + + printf " => done\n" + return 0 +} + +container_consoles() { + printf "#### container_consoles(): setting container consoles ...\n" + + # disable unwanted ttys + if [[ ${tty} < 6 ]]; then + local mindis=$(( ${tty} + 1 )) + sed -i "s/^c[${mindis}-6]/#&/" "${rootfs}/etc/inittab" + fi + printf " => main console + ${tty} ttys\n" + + if [[ -z "${autologin}" ]]; then + sed 's/agetty -a root/agetty/' -i "${rootfs}/etc/inittab" + elif [[ "${user}" != "root" ]]; then + sed "s/agetty -a root/agetty -a ${user}/" -i "${rootfs}/etc/inittab" + printf " => Autologin on main console for %s enabled\n" "${user}" + [[ -z "${forced_password}" ]] && unset password + store_user_message "${user} has autologin on main console" + else + printf " => Autologin on main console for root enabled\n" + [[ -z "${forced_password}" ]] && unset password + store_user_message "${user} has autologin on main console" + fi + printf " => done\n" +} + +container_tz() +{ + printf "#### container_tz(): setting container timezone ...\n" + + #let's try to copy it from host + if [ -L "/etc/localtime" ]; then + #host has a symlink + #let see if we can reproduct symlink + target=$(readlink /etc/localtime) + if [[ "$target" != "" ]]; then + if [ -f "${rootfs}/${target}" ]; then + #same target exists in container + chroot "${rootfs}" ln -sf "${target}" "/etc/localtime" + printf " => host symlink reproducted in container : %s\n" "${target}" + store_user_message "timezone copyed from host" + return 0 + fi + fi + fi + + if [ -e /etc/localtime ]; then + # duplicate host timezone + cat /etc/localtime > "${rootfs}/etc/localtime" + printf " => host localtime copyed to container\n" + store_user_message "timezone was staticly copyed from host" + else + # otherwise set up UTC + chroot "${rootfs}" ln -sf /usr/share/zoneinfo/UTC /etc/localtime + printf " => fallback: fixed to UTC\n" + store_user_message "timezone was fixed to UTC" + fi +} + + +container_portage() +{ + printf "#### container_portage(): setting container portage... \n" + + #default entry for conf + portage_mount="#container set with private portage tree, no mount here" + + printf "Warnings are normal here, don't worry\n" + #container repos detection + if chroot ${rootfs} portageq get_repo_path / gentoo > /dev/null ; then + portage_container="$(chroot ${rootfs} portageq get_repo_path / gentoo)" + else + die 1 "Failed to figure out container portage tree location with portageq get_repo_path / gentoo\n" + fi + + if [[ -n "${private_portage}" ]]; then + container_private_portage + return 0 + fi + + if [ -z "${portage_dir}" ]; then + #gentoo host detection + printf "trying to guess portage_dir from host...\n" + portage_dir="$(portageq get_repo_path / gentoo 2>/dev/null)" + if [ ! -d "${portage_dir}/profiles" ]; then + printf " => host portage detection failed (not gentoo host), fallback to private portage tree\n" + container_private_portage + return 0 + fi + else + if [ ! -d "${portage_dir}/profiles" ]; then + die 1 "specified portage_dir (%s) does not contains profiles, is it a portage tree ?\n" "${portage_dir}" + fi + fi + + printf "trying to guess portage distfiles dir from host ...\n" + portage_distfiles_dir="$(portageq distdir 2>/dev/null)" + if [ ! -d "${portage_distfiles_dir}" ]; then + portage_distfiles_dir="${portage_dir}/distfiles" + fi + + # if we are here, we have shared portage_dir + #ensure dir exists + chroot "${rootfs}" mkdir ${portage_container} + portage_mount="#container set with shared portage +lxc.mount.entry=${portage_dir} ${portage_container/\//} none ro,bind 0 0 +lxc.mount.entry=${portage_distfiles_dir} ${portage_container/\//}/distfiles none rw,bind 0 0 +#If you use eix, you should uncomment this +#lxc.mount.entry=/var/cache/eix var/cache/eix none ro,bind 0 0" + store_user_message "container has a shared portage from host's ${portage_dir} to ${portage_container/\//}" + #Let's propose binary packages + cat <<- EOF >> "${rootfs}/etc/portage/make.conf" +# enable this to store built binary packages +#FEATURES="\$FEATURES buildpkg" + +# enable this to use built binary packages +#EMERGE_DEFAULT_OPTS="\${EMERGE_DEFAULT_OPTS} --usepkg" + +# enable and *tune* this kind of entry to slot binaries, specialy if you use multiples archs and variants +#PKGDIR="\${PKGDIR}/amd64 +#or PKGDIR="\${PKGDIR}/hardened" +EOF + printf " => portage stuff done, see /etc/portage/make.conf for additional tricks\n" + +} + +container_private_portage() +{ + #called from container_portage() do not call directly from container_setup + printf "# untaring private portage to %s from %s ... \n" "${rootfs}/${portage_container}" "${portage_cache}" + mkdir -p "${rootfs}/${portage_container}" + execute_exclusively portage 60 \ + tar -xp --strip-components 1 -C "${rootfs}/${portage_container}" \ + -f "${portage_cache}" --numeric-owner \ + || die 2 "Error: unable to extract the portage tree.\n" + store_user_message "container has its own portage tree at ${portage_container}" + printf "=> done\n" +} + +#helper func for container_genconf_net() +nic_write() +{ + #display with gentoo's confd.net format + echo "config_${nic_name}=\"${nic_conf}\"" + #add to managed list + [[ "${nic_conf}" == "dhcp" ]] && nic_managed="${nic_managed} ${nic_name}" + [[ "${nic_conf}" == "null" ]] && nic_unmanaged="${nic_unmanaged} ${nic_name}" + [[ -z "${nic_hwaddr}" && ${nic_type} == "veth" ]] && nic_wo_hwaddr="${nic_wo_hwaddr} ${nic_name}" + nic_writed=1 +} + +#Analyse lxc.conf and print conf.d/net content +container_conf_net() +{ + local file=${1} + [[ -z "${nic_last}" ]] && nic_last=-1 + [[ -z "${nic_named}" ]] && nic_named=0 + OLDIFS=$IFS + IFS=" +" + #let's do some drity bash things to parse lxc network conf + for line in $( sed -r "s/[ ]*=[ ]*/_real_ugly_sep_42_/" "${file}" ); do + key=$(echo "${line}" | sed 's/_real_ugly_sep_42_.*$//') + value=$(echo "${line}" | sed 's/^.*_real_ugly_sep_42_//') + + #new nic ! + if [[ "${key}" == "lxc.net.0.type" ]]; then + #we don't know what to do with it. + [[ "${value}" == "empty" ]] && continue + + #write conf from previous loops + [[ "${nic_writed}" == "0" ]] && nic_write + + #init defaults + let nic_last=nic_last+1 + + nic_writed=0 + #if 1 named between 2 not named: last is eth1 + #=> Number is ID munis number of named NIC before + nic_name="eth$(( ${nic_last} - ${nic_named} ))" + nic_conf="dhcp" + nic_type="${value}" + fi + + if [[ "${key}" == "lxc.net.0.hwaddr" ]]; then + nic_hwaddr=1 + fi + + if [[ "${key}" =~ ^lxc.net.0.ipv(4|6) ]]; then + #tell openrc to not manage this NIC as LXC set there address + nic_conf="null" + fi + if [[ "${key}" =~ ^lxc.net.0.name ]]; then + nic_name="${value}" + let nic_named=nic_named+1 + fi + if [[ "${key}" == "lxc.include" ]]; then + #recursive into include + container_conf_net "${value}" + fi + done + #write conf from previous loops + [[ "${nic_writed}" == "0" ]] && nic_write + IFS=$OLDIFS +} + +container_net() +{ + printf "container_net(): setting container network conf... \n" + + #Analyse network configuration in config + container_conf_net "$path/config" >> "${rootfs}/etc/conf.d/net" + + # found how much nic finally have + nic_count=$(( ${nic_last} + 1 )) + + # unless openrc manage a nic, we now have to force openrc to automatic + # provision of the 'net' dep. If we do not, network dependent services + # will fail to load + if [[ -z "${nic_managed}" ]]; then + #tell openrc that lxc already did the work + echo 'rc_provide="net"' >> "${rootfs}/etc/rc.conf" + fi + + #No NIC ? + if [[ ${nic_count} == 0 ]]; then + #If no Nic, no need to continue + bridge=$(brctl show | awk 'NR==2 {print $1}') + if [[ "${bridge}" != "" ]]; then + store_user_message "No network interface for this container +It's a pitty, you have bridge, ${bridge}. +If it is for Lxc, use it next time by adding this to your default.conf : +lxc.net.0.type = veth +lxc.net.0.link = ${bridge} +lxc.net.0.flags = up +lxc.net.0.hwaddr = fe:xx:xx:xx:xx:xx" + return 0 + else + store_user_message "No network interface for this container" + return 0 + fi + fi + + #For each openrc managed nic, activate + sys_nic_index=1 + for nic in ${nic_managed} + do + chroot "${rootfs}" ln -s net.lo "/etc/init.d/net.${nic}" + chroot "${rootfs}" rc-update add net.${nic} default + #fake sysfs for openrc, in case settings does not provide it + mkdir -p "${rootfs}/sys/class/net/${nic}" + echo ${sys_nic_index} > "${rootfs}/sys/class/net/${nic}/ifindex" + echo up > "${rootfs}/sys/class/net/${nic}/operstate" + let sys_nic_index=sys_nic_index+1 + done + + #Warn about dynamic hwaddr + if [[ -n "${nic_wo_hwaddr}" ]]; then + store_user_message "Warning, these veth NIC don't have fixed hwaddr : +${nic_wo_hwaddr} + +see http://lists.linuxcontainers.org/pipermail/lxc-devel/2013-December/006736.html +and man lxc.conf" + fi + + printf " => network conf done.\n" +} + +# custom hostname +container_hostname() +{ + printf "#### container_hostname(): setting hostname... \n" + printf "hostname=\"%s\"\n" "${name}" > "${rootfs}/etc/conf.d/hostname" + printf " => done.\n" +} + +container_auth() +{ + printf "#### container_auth(): setting authentification... \n" + if [[ "${user}" != "root" ]]; then + printf " non root user requested, creating... \n" + chroot "${rootfs}" useradd --create-home -s /bin/bash "${user}" || die 1 "failed to create user ${user}" + printf " => user %s created\n" "${user}" + fi + store_user_message "Connection user is ${user}" + #Home of user + auth_home=$(chroot "${rootfs}" getent passwd "${user}" | cut -d : -f 6) + if [[ -r "${auth_key}" ]]; then + printf " deploying auth_key %s for user %s ...\n" "${auth_key}" "${user}" + mkdir -p "${rootfs}/${auth_home}/.ssh" + cat "${auth_key}" >> "${rootfs}/${auth_home}/.ssh/authorized_keys" + chroot "${rootfs}" chown "${user}:" "${auth_home}/.ssh/authorized_keys" + printf " => inserted public key in %s/.ssh/authorized_keys\n" "${auth_home}" + [[ -z "${forced_password}" ]] && unset password + store_user_message "${user} has the ssh key you gave us" + fi + + if [[ -n "${password}" ]]; then + printf " setting password for %s ...\n" "${user}" + echo "${user}:${password}" | chroot "${rootfs}" chpasswd || die 1 "failed to change password" + printf " => done. if you didn't specify , default is 'toor'\n" + if [[ -n "${forced_password}" ]]; then + store_user_message "${user} has the password you give for him" + fi + fi + + printf " => done.\n" +} + +container_sshd() { + printf "#### container_sshd(): enabling sshd... \n" + + chroot "${rootfs}" rc-update add sshd || die 1 "failed to enable sshd\n" + + printf " => done.\n" +} + +################################################################################ +# lxc configuration files +################################################################################ + +container_conf() +{ + printf "container_configuration(): making lxc configuration file... \n" + + #at this point if there + conf_file="${path}/config" + + # if there is exactly one veth network entry, make sure it has an + # associated hwaddr. + nics=`grep -e '^lxc\.net\.0\.type[ \t]*=[ \t]*veth' ${conf_file} | wc -l` + if [ $nics -eq 1 ]; then + grep -q "^lxc.net.0.hwaddr" ${conf_file} || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" ${conf_file} + fi + + if grep -q "^lxc.rootfs.path" "${conf_file}" ; then + #lxc-create already provided one + conf_rootfs_line="" + else + conf_rootfs_line="lxc.rootfs.path = $(readlink -f "${rootfs}")" + fi + if [[ "${arch}" == "x86" || "${arch}" == "amd64" ]]; then + local conf_arch_line="lxc.arch = ${arch}" + else + local conf_arch_line="# lxc.arch = ${arch}" + fi + + cat <<- EOF >> "${conf_file}" +# sets container architecture +# If desired architecture != amd64 or x86, then we leave it unset as +# LXC does not oficially support anything other than x86 or amd64. +${conf_arch_line} + +# set the hostname +lxc.uts.name = ${name} +lxc.tty.max = ${tty} + +${conf_rootfs_line} +${portage_mount} +${conf_sysfs} +${conf_mounts} + +lxc.include = ${LXC_TEMPLATE_CONFIG}/gentoo.${settings}.conf +EOF + printf " => done.\n" +} + +usage() +{ + cat <] [-v|--variant ] [-P|--private-portage] [--portage-dir ] [-t|--tarball ] + [-F|--flush-cache] [-c|--cache-only] [-u|--user ] [-w|--password ] [--autologin] [-S|--auth-key ] + [-s|--settings ] [-m|--mirror ] [--tty ] + +arch: the container architecture (e.g. amd64): defaults to host arch (currently: '${arch}') + If you choose one that needs emulation + tested: amd64, x86 + You could try any other gentoo arch, why not... + +variant: gentoo's Architecture variant as of dec 2013 : (currently: '${variant}') + for amd64 arch: amd64 (default), amd64-hardened+nomultilib, amd64-hardened, amd64-nomultilib, x32 + for x86 arch: i686 (default), i486, i686-hardened + for arm arch: armv7a (default), armv7a_hardfp, armv6j, armv6j_hardfp, armv5tel, armv4tl + +private-portage: by default, /usr/portage is mount-binded with host one if exists (currently: '${private_portage}') + this force container to have his own copy + +portage-dir: portage dir used for shared portage + by default the host on if any (currently: '${portage_dir}') + +tarball: force usage of local stage3 archive (currently: '${arch}') + If empty, latest will be downloaded + +flush-cache: do like there is no previous cache + +cache-only: just ensure cache is present + if cache exists and "flush-cache" not specified, does nothing + +user: user used in auth oriented options (currently: '${user}') + +password: password for user (currently: '${password}') + if default, usage of auth-key will disable password setting + +autologin: enable autologin for user (currently: '${autologin}') + This unset default password setting + +auth-key: SSH Public key file to inject into container for user (currently: '${auth_key}') + This unset default password setting + +settings: choose common configuration (currently: '${settings}') + see ${LXC_TEMPLATE_CONFIG}/gentoo.*.conf + Available settings: + $(ls -1 ${LXC_TEMPLATE_CONFIG}/gentoo.*.conf | xargs basename -a -s .conf | sed 's/^gentoo.//') + +mirror: gentoo mirror for download (currently: '${mirror}') + +tty: number of tty (6 max) (currently: '${tty}') +EOF + exit 0 +} + +#some overridable defaults +set_default_arch + +mirror="http://distfiles.gentoo.org" +user="root" +tty=1 +settings="common" +options=$(getopt -o hp:n:a:FcPv:t:S:u:w:s:m: -l help,rootfs:,path:,name:,arch:,flush-cache,cache-only,private-portage,variant:,portage-dir:,tarball:,auth-key:,user:,autologin,password:,settings:,mirror:,tty: -- "$@") + +eval set -- "$options" + +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + --rootfs) rootfs=$2; shift 2;; + -p|--path) path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -F|--flush-cache) flush_cache=1; shift 1;; + -c|--cache-only) cache_only=1; shift 1;; + -P|--private-portage) private_portage=1; shift 1;; + -v|--variant) variant=$2; shift 2;; + --portage-dir) portage_dir=$2; shift 2;; + -t|--tarball) tarball=$2; shift 2;; + -S|--auth-key) auth_key=$2; shift 2;; + -u|--user) user=$2; shift 2;; + -w|--password) forced_password=1; password=$2; shift 2;; + -s|--settings) settings=$2; shift 2;; + -m|--mirror) mirror=$2; shift 2;; + --container-cache) containercache=$2; shift 2;; + --tty) [[ $2 -lt 6 ]] && tty=$2; shift 2;; + --autologin) autologin=1; shift 1;; + --) shift 1; break ;; + *) break ;; + esac +done + +# Allow the cache path to be set by environment variable +cacheroot="${LXC_CACHE_PATH:-"@LOCALSTATEDIR@/cache/lxc"}/gentoo" +portage_cache="${cacheroot}/portage.tbz" +cachefs="${cacheroot}/rootfs-${arch}-${variant}" + +alias wget="wget --timeout=8 --read-timeout=15 -c -t10 -nd" + +do_all() { + cache_setup + if [ -z "${cache_only}" ]; then + container_setup + fi +} + +execute_exclusively "cache-${arch}-${variant}" 60 do_all diff --git a/templates/lxc-openmandriva.in b/templates/lxc-openmandriva.in new file mode 100644 index 0000000..2761cd5 --- /dev/null +++ b/templates/lxc-openmandriva.in @@ -0,0 +1,484 @@ +#!/bin/bash + +# +# template script for generating openmandriva container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# Alexander Khryukin +# Vokhmin Alexey V + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +#Configurations +#distro=cooker +hostarch=$(uname -m) +# Allow the cache base to be set by environment variable +cache_base="${LXC_CACHE_PATH:-@LOCALSTATEDIR@/cache/lxc/openmandriva/$arch}" +default_path=@LXCPATH@ +default_profile=default +lxc_network_type=veth +lxc_network_link=br0 + +# is this openmandriva? +[ -f /etc/mandriva-release ] && is_openmandriva=true + +configure_openmandriva() +{ +mkdir -p ${rootfs_path}/etc/sysconfig/network-scripts/ + + # configure the network using the dhcp + cat < ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0 +DEVICE=eth0 +ONBOOT=yes +BOOTPROTO=dhcp +NM_CONTROLLED=no +HOSTNAME=${utsname} +EOF + + # set the hostname + cat < ${rootfs_path}/etc/sysconfig/network +NETWORKING=yes +HOSTNAME=${utsname} +EOF + +echo "${utsname}" > ${rootfs_path}/etc/hostname + + # set minimal hosts + cat < $rootfs_path/etc/hosts +127.0.0.1 localhost.localdomain localhost $utsname +::1 localhost6.localdomain6 localhost6 +EOF +} + +populate_dev() +{ + echo -n "Create devices in /dev/" + dev_path="${rootfs_path}/dev" + rm -rf $dev_path + mkdir -p $dev_path + mknod -m 666 ${dev_path}/null c 1 3 + mknod -m 666 ${dev_path}/zero c 1 5 + mknod -m 666 ${dev_path}/random c 1 8 + mknod -m 666 ${dev_path}/urandom c 1 9 + mkdir -m 755 ${dev_path}/pts + mkdir -m 1777 ${dev_path}/shm + mknod -m 666 ${dev_path}/tty c 5 0 + mknod -m 666 ${dev_path}/tty0 c 4 0 + mknod -m 666 ${dev_path}/tty1 c 4 1 + mknod -m 666 ${dev_path}/tty2 c 4 2 + mknod -m 666 ${dev_path}/tty3 c 4 3 + mknod -m 666 ${dev_path}/tty4 c 4 4 + mknod -m 600 ${dev_path}/console c 5 1 + mknod -m 666 ${dev_path}/full c 1 7 + mknod -m 600 ${dev_path}/initctl p + mknod -m 666 ${dev_path}/ptmx c 5 2 + mkdir -m 755 ${dev_path}/net + mknod -m 666 ${dev_path}/net/tun c 10 200 + +} + +set_guest_root_password() +{ + [ -z "$root_password" ] && return # pass is empty, abort + + echo " - setting guest root password.." + echo "root passwd is: $root_password" + echo "root:$root_password" | chroot "$rootfs_path" chpasswd + echo "done." +} + +create_chroot_openmandriva() +{ + # check the mini openmandriva was not already downloaded + INSTALL_ROOT=$cache/cache + mkdir -p $INSTALL_ROOT + if [ $? -ne 0 ]; then + echo "Failed to create '$INSTALL_ROOT' directory" + return 1 + fi + # package list to install + PKG_LIST="basesystem-minimal locales locales-en initscripts urpmi cronie dhcp-client kbd" + # download a mini openmandriva into a cache + echo "Downloading openmandriva minimal ..." + URPMI="/usr/sbin/urpmi.addmedia --urpmi-root $INSTALL_ROOT main http://abf.rosalinux.ru/downloads/$release/repository/$arch/main/release" + echo $URPMI + URPMI_BASE="/usr/sbin/urpmi --no-suggests --no-verify-rpm --ignorearch --root $INSTALL_ROOT --urpmi-root $INSTALL_ROOT --auto $PKG_LIST" + $URPMI + $URPMI_BASE + # We're splitting the old loop into two loops plus a directory retrival. + # First loop... Try and retrive a mirror list with retries and a slight + # delay between attempts... + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + + mv "$INSTALL_ROOT" "$cache/rootfs" + echo "Download complete." + + return 0 + +} + +copy_openmandriva() +{ + + echo -n "Copying rootfs to $rootfs_path ..." + mkdir -p $rootfs_path + rsync -SHaAX $cache/rootfs/ $rootfs_path/ + return 0 +} + +update_openmandriva() +{ + echo "automated update in progress..." + urpmi --root $cache/rootfs --urpmi-root $cache/rootfs --auto --auto-update --ignorearch +} + +configure_openmandriva_systemd() +{ + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/proc-sys-fs-binfmt_misc.automount + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/systemd-udevd.service + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/systemd-udevd-control.socket + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/systemd-udevd-kernel.socket + # remove numlock service + # KDGKBLED: Inappropriate ioctl for device + rm -f ${rootfs_path}/etc/systemd/system/getty@.service.d/enable-numlock.conf + + unlink ${rootfs_path}/etc/systemd/system/default.target + chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target + sed -i 's!ConditionPathExists=/dev/tty0!ConditionPathExists=|/dev/tty0\nConditionVirtualization=|lxc!' \ + ${rootfs_path}/lib/systemd/system/getty\@.service +} + + +install_openmandriva() +{ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ + ( + flock -x 9 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + echo "Checking cache download in $cache/rootfs ... " + if [ ! -e "$cache/rootfs" ]; then + echo $cache/rootfs + create_chroot_openmandriva + if [ $? -ne 0 ]; then + echo "Failed to download 'openmandriva basesystem-minimal'" + return 1 + fi + else + echo "Cache found. Updating..." + update_openmandriva + if [ $? -ne 0 ]; then + echo "Failed to update 'openmandriva base', continuing with last known good cache" + else + echo "Update finished" + fi + fi + + echo "Copy $cache/rootfs to $rootfs_path ... " + copy_openmandriva + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + return 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-openmandriva + + return $? +} + +copy_configuration() +{ + + mkdir -p $config_path + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs_path" >> $config_path/config + cat <> $config_path/config +lxc.uts.name = $name +lxc.tty.max = 4 +lxc.pty.max = 1024 +lxc.cap.drop = sys_module mac_admin mac_override sys_time +lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +#networking +lxc.net.0.type = $lxc_network_type +lxc.net.0.flags = up +lxc.net.0.link = $lxc_network_link +lxc.net.0.name = eth0 +lxc.net.0.mtu = 1500 +EOF +if [ ! -z ${ipv4} ]; then + cat <> $config_path/config +lxc.net.0.ipv4.address = $ipv4 +EOF +fi +if [ ! -z ${gw} ]; then + cat <> $config_path/config +lxc.net.0.ipv4.gateway = $gw +EOF +fi +if [ ! -z ${ipv6} ]; then + cat <> $config_path/config +lxc.net.0.ipv6.address = $ipv6 +EOF +fi +if [ ! -z ${gw6} ]; then + cat <> $config_path/config +lxc.net.0.ipv6.gateway = $gw6 +EOF +fi + cat <> $config_path/config +#cgroups +lxc.cgroup.devices.deny = a +# /dev/null and zero +lxc.cgroup.devices.allow = c 1:3 rwm +lxc.cgroup.devices.allow = c 1:5 rwm +# consoles +lxc.cgroup.devices.allow = c 5:1 rwm +lxc.cgroup.devices.allow = c 5:0 rwm +lxc.cgroup.devices.allow = c 4:0 rwm +lxc.cgroup.devices.allow = c 4:1 rwm +# /dev/{,u}random +lxc.cgroup.devices.allow = c 1:9 rwm +lxc.cgroup.devices.allow = c 1:8 rwm +lxc.cgroup.devices.allow = c 136:* rwm +lxc.cgroup.devices.allow = c 5:2 rwm +# rtc +lxc.cgroup.devices.allow = c 10:135 rwm +EOF + + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +clean() +{ + + if [ ! -e $cache ]; then + exit 0 + fi + + # lock, so we won't purge while someone is creating a repository + ( + flock -x 9 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + + echo -n "Purging the download cache for OpenMandriva-$release..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-openmandriva +} + +usage() +{ + cat < + [-p|--path=] [-c|--clean] [-R|--release=] + [-4|--ipv4=] [-6|--ipv6=] + [-g|--gw=] [-d|--dns=] + [-P|--profile=] [--rootfs=] + [-A|--arch=] + [-h|--help] +Mandatory args: + -n,--name container name, used to as an identifier for that container from now on +Optional args: + -p,--path path to where the container rootfs will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case + -c,--clean clean the cache + -R,--release openmandriva2013.0/cooker/rosa2012.1 release for the new container. if the host is OpenMandriva, then it will default to the host's release. + -4,--ipv4 specify the ipv4 address to assign to the virtualized interface, eg. 192.168.1.123/24 + -6,--ipv6 specify the ipv6 address to assign to the virtualized interface, eg. 2003:db8:1:0:214:1234:fe0b:3596/64 + -g,--gw specify the default gw, eg. 192.168.1.1 + -G,--gw6 specify the default gw, eg. 2003:db8:1:0:214:1234:fe0b:3596 + -d,--dns specify the DNS server, eg. 192.168.1.2 + -P,--profile Profile name is the file name in /etc/lxc/profiles contained packages name for install to cache. + -A,--arch Define what arch the container will be [i586,x86_64,armv7l,armv7hl] + ---rootfs rootfs path + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o hp:n:P:cR:4:6:g:d:A -l help,rootfs:,path:,name:,profile:,clean:,release:,ipv4:,ipv6:,gw:,dns:,arch: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +release=${release:-"cooker"} +if [ -f /etc/lsb-release ]; then + . /etc/lsb-release + if [ "$DISTRIB_ID" = "OpenMandrivaLinux" ]; then + release=openmandriva2013.0 + elif [ "$DISTRIB_ID" = "RosaDesktop.Fresh" ]; then + release=rosa2012.1 + else + echo "This is not an OpenMandriva or ROSA release" + exit 1 + fi +fi + +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs_path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -P|--profile) profile=$2; shift 2;; + -c|--clean) clean=1; shift 1;; + -R|--release) release=$2; shift 2;; + -A|--arch) arch=$2; shift 2;; + -4|--ipv4) ipv4=$2; shift 2;; + -6|--ipv6) ipv6=$2; shift 2;; + -g|--gw) gw=$2; shift 2;; + -d|--dns) dns=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +arch=${arch:-$hostarch} + +if [ ! -z "$clean" -a -z "$path" ]; then + clean || exit 1 + exit 0 +fi + +if [ -z "${utsname}" ]; then + utsname=${name} +fi + +type urpmi >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "'urpmi' command is missing" + exit 1 +fi + +if [ -z "$path" ]; then + path=$default_path +fi + +if [ -z "$profile" ]; then + profile=$default_profile +fi + +if [ $hostarch = "i586" -a $arch = "x86_64" ]; then + echo "can't create x86_64 container on i586" + exit 1 +fi + +if [ -z "$ipv4" -a -z "$ipv6" ]; then + BOOTPROTO="dhcp" +else + BOOTPROTO="static" +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +# check for 'lxc.rootfs.path' passed in through default config by lxc-create +if [ -z "$rootfs_path" ]; then + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config) + else + rootfs_path=$path/$name/rootfs + fi +fi + +config_path=$default_path/$name +cache=$cache_base/$release/$arch/$profile + +if [ ! -f $config_path/config ]; then + echo "A container with that name exists, chose a different name" + exit 1 +fi + +install_openmandriva +if [ $? -ne 0 ]; then + echo "failed to install openmandriva" + exit 1 +fi + +configure_openmandriva +if [ $? -ne 0 ]; then + echo "failed to configure openmandriva for a container" + exit 1 +fi + +# If the systemd configuration directory exists - set it up for what we need. +if [ -d ${rootfs_path}/etc/systemd/system ] +then + configure_openmandriva_systemd +fi + +populate_dev +if [ $? -ne 0 ]; then + echo "failed to populated /dev/ devices" + exit 1 +fi + +set_guest_root_password +if [ $? -ne 0 ]; then + echo "failed to configure password for chroot" + exit 1 +fi + +copy_configuration +if [ $? -ne 0 ]; then + echo "failed write configuration file" + exit 1 +fi + +if [ ! -z "$clean" ]; then + clean || exit 1 + exit 0 +fi +echo "container rootfs and config created" diff --git a/templates/lxc-opensuse.in b/templates/lxc-opensuse.in new file mode 100644 index 0000000..c2480dd --- /dev/null +++ b/templates/lxc-opensuse.in @@ -0,0 +1,557 @@ +#!/bin/bash + +# +# template script for generating OpenSUSE container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# Daniel Lezcano +# Frederic Crozat +# Michael H. Warfield +# Johannes Kastl +# Thomas Lamprecht + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +if [ -x /usr/bin/obs-build ]; then + BUILD=/usr/bin/obs-build + export BUILD_DIR=/usr/lib/obs-build +else + BUILD=/usr/bin/build + export BUILD_DIR=/usr/lib/build +fi + +configure_opensuse() +{ + rootfs=$1 + hostname=$2 + + # set first network adapter as dhcp. This is the most common config. + cat < $rootfs/etc/sysconfig/network/ifcfg-eth0 +STARTMODE='auto' +BOOTPROTO='dhcp' +EOF + + # create empty fstab + touch $rootfs/etc/fstab + + # set the hostname + cat < $rootfs/etc/HOSTNAME +$hostname +EOF + # ensure /etc/hostname is available too + ln -s -f HOSTNAME $rootfs/etc/hostname + + # do not use hostname from HOSTNAME variable + cat <> $rootfs/etc/sysconfig/cron +unset HOSTNAME +EOF + + # set minimal hosts + cat < $rootfs/etc/hosts +127.0.0.1 localhost $hostname +EOF + + # disable yast->bootloader in container + cat < $rootfs/etc/sysconfig/bootloader +LOADER_TYPE=none +LOADER_LOCATION=none +EOF + + # set /dev/console as securetty + cat << EOF >> $rootfs/etc/securetty +console +EOF + + cat <> $rootfs/etc/sysconfig/boot +# disable root fsck +ROOTFS_FSCK="0" +ROOTFS_BLKDEV="/dev/null" +EOF + + + # remove pointless services in a container + ln -s /dev/null $rootfs/etc/systemd/system/proc-sys-fs-binfmt_misc.automount + ln -s /dev/null $rootfs/etc/systemd/system/console-shell.service + ln -s /dev/null $rootfs/etc/systemd/system/systemd-vconsole-setup.service + # enable getty and console services + sed -e 's/ConditionPathExists=.*//' $rootfs/usr/lib/systemd/system/getty@.service > $rootfs/etc/systemd/system/getty@.service + ln -s getty@.service $rootfs/etc/systemd/system/getty@tty1.service + mkdir -p $rootfs/etc/systemd/system/getty.target.wants/ + ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@console.service + ln -s -f ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty1.service + ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty2.service + ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty3.service + ln -s ../getty@.service $rootfs/etc/systemd/system/getty.target.wants/getty@tty4.service + + touch $rootfs/etc/sysconfig/kernel + + echo "Please change root-password !" + + return 0 +} + +download_opensuse() +{ + cache=$1 + arch=$2 + + if [ ! -x ${BUILD} ]; then + echo "Could not create openSUSE template :" + echo "you need to install \"build\" package" + return 1 + fi + + # check the mini opensuse was not already downloaded + mkdir -p "$cache/partial-$arch" + + if [ $? -ne 0 ]; then + echo "Failed to create '$cache/partial-$arch' directory" + return 1 + fi + + # download a mini opensuse into a cache + echo "Downloading opensuse minimal ..." + mkdir -p "$cache/partial-$arch-packages" + + oss_repo_url="http://download.opensuse.org/distribution/$DISTRO/repo/oss/" + if [[ $DISTRO == "tumbleweed" ]]; then + oss_repo_url="http://download.opensuse.org/$DISTRO/repo/oss/" + fi + zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar "$oss_repo_url" repo-oss || return 1 + + update_repo_url="http://download.opensuse.org/update/$DISTRO/repo/oss" + # Leap update repos were rearranged + if [[ $DISTRO == "leap/4"* ]]; then + update_repo_url="http://download.opensuse.org/update/$DISTRO/oss/" + fi + # tumbleweed has no update repo + if [[ $DISTRO != "tumbleweed" ]]; then + zypper --quiet --root $cache/partial-$arch-packages --non-interactive ar "$update_repo_url" update || return 1 + fi + + zypper --quiet --root $cache/partial-$arch-packages --non-interactive --gpg-auto-import-keys update || return 1 + zypper --root $cache/partial-$arch-packages --non-interactive in --auto-agree-with-licenses --download-only zypper lxc patterns-openSUSE-base bash iputils sed tar rsyslog || return 1 + + cat > $cache/partial-$arch-packages/opensuse.conf << EOF +Preinstall: aaa_base bash coreutils diffutils +Preinstall: filesystem fillup glibc grep insserv-compat perl-base +Preinstall: libbz2-1 pam +Preinstall: permissions rpm sed tar libz1 libselinux1 +Preinstall: liblzma5 libcap2 libacl1 libattr1 +Preinstall: libpopt0 libelf1 +Preinstall: libpcre1 + +RunScripts: aaa_base + +Support: zypper +Support: patterns-openSUSE-base +Support: lxc +Support: ncurses-utils +Support: iputils +Support: udev +Support: netcfg +Support: hwinfo insserv-compat module-init-tools openSUSE-release openssh +Support: pwdutils sysconfig + +Ignore: rpm:suse-build-key,build-key +Ignore: systemd:systemd-presets-branding +EOF + + if [ $DISTRO = "13.2" ] + then + echo "Support: python3-base" >> $cache/partial-$arch-packages/opensuse.conf + fi + + if [[ $DISTRO == "tumbleweed" ]]; then + echo "Preinstall: liblua5_3 libncurses6 libreadline7" >> $cache/partial-$arch-packages/opensuse.conf + else + echo "Preinstall: liblua5_1 libncurses5 libreadline6" >> $cache/partial-$arch-packages/opensuse.conf + echo "Support: rpcbind" >> $cache/partial-$arch-packages/opensuse.conf + fi + + # dhcpcd is not in the default repos since Leap 42.1, neither in tumbleweed + if [[ $DISTRO != "leap/4"* ]] && [[ $DISTRO != "tumbleweed" ]]; then + echo "Support: dhcpcd" >> $cache/partial-$arch-packages/opensuse.conf + fi + + # Leap and tumbleweed doesn't seem to have iproute2 utils installed + if [[ $DISTRO == "leap/4"* ]] || [[ $DISTRO == "tumbleweed" ]]; then + echo "Support: net-tools iproute2" >> $cache/partial-$arch-packages/opensuse.conf + fi + + if [ "$arch" = "i686" ]; then + mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/i686/ + for i in "$cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/i586/*" ; do + ln -s $i $cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/i686/ + done + mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/update/i686 + for i in "$cache/partial-$arch-packages/var/cache/zypp/packages/update/i586/*" ; do + ln -s $i $cache/partial-$arch-packages/var/cache/zypp/packages/update/i686/ + done + fi + + # openSUSE 13.2 has no noarch directory in update + [ -d $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch ] || mkdir -p $cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch + + repos=("--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/$arch" "--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/repo-oss/suse/noarch") + if [[ $DISTRO != "tumbleweed" ]]; then # tumbleweed has no update repo + repos+=("--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/update/$arch" "--repository" "$cache/partial-$arch-packages/var/cache/zypp/packages/update/noarch") + fi + + CLEAN_BUILD=1 BUILD_ARCH="$arch" BUILD_ROOT="$cache/partial-$arch" BUILD_DIST="$cache/partial-$arch-packages/opensuse.conf" PATH="$PATH:$BUILD_DIR" $BUILD_DIR/init_buildsystem --clean --configdir $BUILD_DIR/configs --cachedir $cache/partial-$arch-cache ${repos[*]} || return 1 + + chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar "$oss_repo_url" repo-oss || return 1 + if [[ $DISTRO != "tumbleweed" ]]; then + chroot $cache/partial-$arch /usr/bin/zypper --quiet --non-interactive ar "$update_repo_url" update || return 1 + fi + +# really clean the image + rm -fr $cache/partial-$arch/{.build,.guessed_dist,.srcfiles*,installed-pkg} + rm -fr $cache/partial-$arch/dev +# make sure we have a minimal /dev + mkdir -p "$cache/partial-$arch/dev" + mknod -m 666 $cache/partial-$arch/dev/null c 1 3 + mknod -m 666 $cache/partial-$arch/dev/zero c 1 5 +# create mtab symlink + rm -f $cache/partial-$arch/etc/mtab + ln -sf /proc/self/mounts $cache/partial-$arch/etc/mtab + +# ensure /var/run and /run are symlinked + rm -fr $cache/partial-$arch/var/run + ln -s -f ../run $cache/partial-$arch/var/run + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + + rm -fr "$cache/partial-$arch-packages" + mv "$1/partial-$arch" "$1/rootfs-$arch" + echo "Download complete." + + return 0 +} + +copy_opensuse() +{ + cache=$1 + arch=$2 + rootfs=$3 + + # make a local copy of the mini opensuse + echo "Copying rootfs to $rootfs ..." + mkdir -p $rootfs + rsync -SHaAX $cache/rootfs-$arch/ $rootfs/ || return 1 + return 0 +} + +install_opensuse() +{ + # Allow the cache base to be set by environment variable + cache="${LXC_CACHE_PATH:-@LOCALSTATEDIR@/cache/lxc/opensuse/$DISTRO}" + rootfs=$1 + mkdir -p @LOCALSTATEDIR@/lock/subsys/ + ( + flock -x 9 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + echo "Checking cache download in $cache/rootfs-$arch ... " + if [ ! -e "$cache/rootfs-$arch" ]; then + download_opensuse $cache $arch + if [ $? -ne 0 ]; then + echo "Failed to download 'opensuse base'" + return 1 + fi + fi + + echo "Copy $cache/rootfs-$arch to $rootfs ... " + copy_opensuse $cache $arch $rootfs + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + + return 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-opensuse + + return $? +} + +# Generate a random hardware (MAC) address composed of FE followed by +# 5 random bytes... +create_hwaddr() +{ + openssl rand -hex 5 | sed -e 's/\(..\)/:\1/g; s/^/fe/' +} + +copy_configuration() +{ + path=$1 + rootfs=$2 + name=$3 + + grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo " +lxc.rootfs.path = $rootfs_path +" >> $path/config + + # The following code is to create static MAC addresses for each + # interface in the container. This code will work for multiple + # interfaces in the default config. It will also strip any + # hwaddr stanzas out of the default config since we can not share + # MAC addresses between containers. + # + # This code is largely mimiced from the Fedora Template. + mv $path/config $path/config.def + while read LINE + do + # This should catch variable expansions from the default config... + if expr "${LINE}" : '.*\$' > /dev/null 2>&1 + then + LINE=$(eval "echo \"${LINE}\"") + fi + + # There is a tab and a space in the regex bracket below! + # Seems that \s doesn't work in brackets. + KEY=$(expr "${LINE}" : '\s*\([^ ]*\)\s*=') + + if [[ "${KEY}" != "lxc.net.0.hwaddr" ]] + then + echo "${LINE}" >> $path/config + + if [[ "${KEY}" == "lxc.net.0.link" ]] + then + echo "lxc.net.0.hwaddr = $(create_hwaddr)" >> $path/config + fi + fi + done < $path/config.def + + rm -f $path/config.def + + if [ -e "@LXCTEMPLATECONFIG@/opensuse.common.conf" ]; then + echo " +# Include common configuration +lxc.include = @LXCTEMPLATECONFIG@/opensuse.common.conf +" >> $path/config + fi + + # Append things which require expansion here... + cat <> $path/config +lxc.arch = $arch +lxc.uts.name = $name + +lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed + +# When using LXC with apparmor, uncomment the next line to run unconfined: +lxc.apparmor.profile = unconfined + +# example simple networking setup, uncomment to enable +#lxc.net.0.type = $lxc_network_type +#lxc.net.0.flags = up +#lxc.net.0.link = $lxc_network_link +#lxc.net.0.name = eth0 +# Additional example for veth network type +# static MAC address, +#lxc.net.0.hwaddr = 00:16:3e:77:52:20 +# persistent veth device name on host side +# Note: This may potentially collide with other containers of same name! +#lxc.net.0.veth.pair = v-$name-e0 + +EOF + + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +clean() +{ + cache="${LXC_CACHE_PATH:-@LOCALSTATEDIR@/cache/lxc/opensuse}" + + if [ ! -e $cache ]; then + exit 0 + fi + + # lock, so we won't purge while someone is creating a repository + ( + flock -x 9 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + + echo -n "Purging the download cache..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-opensuse +} + +usage() +{ + cat < -r|--release nn.n --clean +Please give the release as 13.1, 13.2 etc. +If no release is given, openSUSE Leap 42.2 is installed. +EOF + return 0 +} + +# Make arch a global. This may become configurable? +arch=$(uname -m) + +options=$(getopt -o hp:n:r:c -l help,rootfs:,path:,name:,release:,clean -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -r|--release) DISTRO=$2; shift 2;; + -c|--clean) clean=1; shift 1;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ ! -z "$clean" -a -z "$path" ]; then + clean || exit 1 + exit 0 +fi + +type zypper > /dev/null +if [ $? -ne 0 ]; then + echo "'zypper' command is missing" + exit 1 +fi + +if [ -z "$path" ]; then + echo "'path' parameter is required" + exit 1 +fi + +if grep -q Harlequin /etc/os-release || grep -q Tumbleweed /etc/os-release ; then + BVER=`rpm -q --qf '%{version}\n' build` + if [ $? -ne 0 -o "$BVER" -lt "20141120" ]; then + echo "Building openSUSE containers with your version of the build package is broken. Please install the update to version 20141120 or newer." + exit 1 + fi +fi + +if [ -z "$DISTRO" ]; then + echo "" + echo "No release selected, using openSUSE Leap 42.2" + DISTRO="leap/42.2" +else + echo "" + case "$DISTRO" in + 13.1) + echo "Selected openSUSE 13.1" + ;; + + 13.2) + echo "Selected openSUSE 13.2" + ;; + + 42.1|leap/42.1|leap) + echo "Selected openSUSE Leap 42.1" + DISTRO="leap/42.1" + ;; + + 42.2|leap/42.2|422) + echo "Selected openSUSE Leap 42.2" + DISTRO="leap/42.2" + ;; + 42.3|leap/42.3|423) + echo "Selected openSUSE Leap 42.3" + DISTRO="leap/42.3" + ;; + tumbleweed|factory) + echo "Selected openSUSE Leap Tumbleweed" + DISTRO="tumbleweed" + ;; + + *) + echo "You have chosen an invalid release, quitting..." + exit 1 + ;; + esac +fi + + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +# detect rootfs +config="$path/config" +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) + else + rootfs=$path/rootfs + fi +fi + +install_opensuse $rootfs +if [ $? -ne 0 ]; then + echo "failed to install opensuse" + exit 1 +fi + +configure_opensuse $rootfs $name +if [ $? -ne 0 ]; then + echo "failed to configure opensuse for a container" + exit 1 +fi + +copy_configuration $path $rootfs $name +if [ $? -ne 0 ]; then + echo "failed write configuration file" + exit 1 +fi + +if [ ! -z "$clean" ]; then + clean || exit 1 + exit 0 +fi diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in new file mode 100644 index 0000000..afb6902 --- /dev/null +++ b/templates/lxc-oracle.in @@ -0,0 +1,972 @@ +#!/bin/sh +# +# Template script for generating Oracle Enterprise Linux container for LXC +# based on lxc-fedora, lxc-ubuntu +# +# Copyright © 2011 Wim Coekaerts +# Copyright © 2012 Dwight Engen +# +# Modified for Oracle Linux 5 +# Wim Coekaerts +# +# Modified for Oracle Linux 6,7 combined OL4,5,6 into one template script +# Dwight Engen +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +die() +{ + echo "failed: $1" + exit 1 +} + +is_btrfs_subvolume() +{ + if which btrfs >/dev/null 2>&1 && \ + btrfs subvolume list "$1" >/dev/null 2>&1; then + return 0 + fi + return 1 +} + +can_chcon() +{ + if which chcon >/dev/null 2>&1; then + selinuxenabled >/dev/null 2>&1 + return $? + fi + return 1 +} + +# fix up the container_rootfs +container_rootfs_patch() +{ + echo "Patching container rootfs $container_rootfs for Oracle Linux $container_release_major.$container_release_minor" + + # copy ourself into the container to be used to --patch the rootfs when + # yum update on certain packages is done. we do this here instead of in + # container_rootfs_configure() in case the patching done in this function + # is updated in the future, we can inject the updated version of ourself + # into older containers. + if [ $container_rootfs != "/" ]; then + cp -f `readlink -f $0` $container_rootfs/usr/bin/lxc-patch + if [ $container_release_major -lt "6" ]; then + mkdir -p $container_rootfs/usr/lib/yum-plugins + cp @DATADIR@/lxc/lxc-patch.py $container_rootfs/usr/lib/yum-plugins + fi + if [ $container_release_major -ge "6" ]; then + mkdir -p $container_rootfs/usr/share/yum-plugins + cp @DATADIR@/lxc/lxc-patch.py $container_rootfs/usr/share/yum-plugins + fi + mkdir -p $container_rootfs/etc/yum/pluginconf.d + cat < $container_rootfs/etc/yum/pluginconf.d/lxc-patch.conf +[main] +enabled=1 +packages=dbus,initscripts,iptables,openssh-server,setup,selinux-policy,readahead,udev,util-linux,util-linux-ng +EOF + fi + + if [ $container_release_major = "4" ]; then + # yum plugin type of TYPE_INTERFACE works in all releases but gives a + # deprecation warning on major > 4, so we default to TYPE_INTERACTIVE + # and fix it up here + sed -i 's|TYPE_INTERACTIVE|TYPE_INTERFACE|' $container_rootfs/usr/lib/yum-plugins/lxc-patch.py + if [ -f $container_rootfs/etc/yum.repos.d/ULN-Base.repo ]; then + mv $container_rootfs/etc/yum.repos.d/ULN-Base.repo \ + $container_rootfs/etc/yum.repos.d/ULN-Base.repo.lxc-disabled + fi + echo "plugins = 1" >>$container_rootfs/etc/yum.conf + fi + + # "disable" selinux in the guest. The policy in the container isn't + # likely to match the hosts (unless host == guest exactly) and the + # kernel can only be enforcing one policy. + # + # The OL 5 init honors /etc/selinux/config, but note that + # this doesnt actually disable it if it's enabled in the host, since + # libselinux::is_selinux_enabled() in the guest will check + # /proc/filesystems and see selinuxfs, thus reporting that it is on + # (ie. check the output of sestatus in the guest). We also replace + # /usr/sbin/selinuxenabled with a symlink to /bin/false so that init + # scripts (ie. mcstransd) that call that think selinux is disabled. + mkdir -p $container_rootfs/selinux + echo 0 > $container_rootfs/selinux/enforce + if [ -e $container_rootfs/etc/selinux/config ]; then + sed -i 's|SELINUX=enforcing|SELINUX=disabled|' $container_rootfs/etc/selinux/config + else + mkdir -p $container_rootfs/etc/selinux + echo "SELINUX=disabled" >$container_rootfs/etc/selinux/config + fi + sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/login + sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/login + sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/sshd + sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/sshd + + # setting /proc/$$/loginuid doesn't work under user namespace, which + # prevents logins from working + sed -i 's|session[ \t]*required[ \t]*pam_loginuid.so|#session required pam_loginuid.so|' $container_rootfs/etc/pam.d/sshd + sed -i 's|session[ \t]*required[ \t]*pam_loginuid.so|#session required pam_loginuid.so|' $container_rootfs/etc/pam.d/login + + if [ -f $container_rootfs/usr/sbin/selinuxenabled ]; then + mv $container_rootfs/usr/sbin/selinuxenabled $container_rootfs/usr/sbin/selinuxenabled.lxcorig + ln -s /bin/false $container_rootfs/usr/sbin/selinuxenabled + fi + + # ensure /dev/ptmx refers to the newinstance devpts of the container, or + # pty's will get crossed up with the hosts (https://lkml.org/lkml/2012/1/23/512) + rm -f $container_rootfs/dev/ptmx + ln -s pts/ptmx $container_rootfs/dev/ptmx + + # OL7 has systemd, no rc.sysinit + if [ $container_release_major = "7" ]; then + # with newer systemd (OL7.2), getty service include container-getty.service + # let that be the one who manage the getty service instead + if [ ! -f $container_rootfs/usr/lib/systemd/system/container-getty@.service ]; then + # from mhw in the fedora template: We do need to disable the + # "ConditionalPathExists=/dev/tty0" line or no gettys are started on + # the ttys in the container. Lets do it in an override copy of the + # service so it can still pass rpm verifies and not be automatically + # updated by a new systemd version. + sed -e 's/^ConditionPathExists=/#LXC ConditionPathExists=/' \ + < $container_rootfs/usr/lib/systemd/system/getty\@.service \ + > $container_rootfs/etc/systemd/system/getty\@.service + # Setup getty service on the 4 ttys we are going to allow in the + # default config. Number should match lxc.tty + ( cd $container_rootfs/etc/systemd/system/getty.target.wants + for i in 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${i}.service; done ) + # We only want to spawn a getty on /dev/console in lxc, libvirt-lxc + # symlinks /dev/console to /dev/tty1 + sed -i '/Before=getty.target/a ConditionVirtualization=lxc' $container_rootfs/usr/lib/systemd/system/console-getty.service + fi + + # disable some systemd services, set default boot, sigpwr target + rm -f $container_rootfs/usr/lib/systemd/system/sysinit.target.wants/kmod-static-nodes.service + chroot $container_rootfs systemctl -q disable graphical.target + chroot $container_rootfs systemctl -q enable multi-user.target + + # systemd in userns won't be able to set /proc/self/oom_score_adj which + # prevents the dbus service from starting + sed -i 's|^OOMScoreAdjust|#LXC OOMScoreAdjust|' $container_rootfs/usr/lib/systemd/system/dbus.service + return + fi + + # silence error in checking for selinux + sed -i 's|cat /proc/self/attr/current|cat /proc/self/attr/current 2>/dev/null|' $container_rootfs/etc/rc.sysinit + sed -i 's|cat /proc/self/attr/current|cat /proc/self/attr/current 2>/dev/null|' $container_rootfs/etc/rc.d/rc.sysinit + + # on ol4 pam_limits prevents logins when using user namespaces + if [ $container_release_major = "4" ]; then + sed -i 's|session[ \t]*required[ \t]*/lib/security/\$ISA/pam_limits.so|#session required /lib/security/$ISA/pam_limits.so|' $container_rootfs/etc/pam.d/system-auth + fi + + # avoid error in ol5 attempting to copy non-existent resolv.conf + if [ $container_release_major = "5" ]; then + sed -i 's|resolv.conf.predhclient|resolv.conf.predhclient 2>/dev/null|' $container_rootfs/sbin/dhclient-script + fi + + # disable interactive ovmd asking questions + if [ -f $container_rootfs/etc/sysconfig/ovmd ]; then + sed -i 's|INITIAL_CONFIG=yes|INITIAL_CONFIG=no|' $container_rootfs/etc/sysconfig/ovmd + fi + + # disable disabling of ipv4 forwarding and defrag on shutdown since + # we mount /proc/sys ro + if [ $container_release_major = "5" ]; then + sed -i 's|-f /proc/sys/net/ipv4/ip_forward|-w /proc/sys/net/ipv4/ip_forward|' $container_rootfs/etc/rc.d/init.d/network + sed -i 's|-f /proc/sys/net/ipv4/ip_always_defrag|-w /proc/sys/net/ipv4/ip_always_defrag|' $container_rootfs/etc/rc.d/init.d/network + fi + + # disable ipv6 on ol6 + rm -f $container_rootfs/etc/sysconfig/network-scripts/init.ipv6-global + + # remove module stuff for iptables it just shows errors that are not + # relevant in a container + if [ -f "$container_rootfs/etc/sysconfig/iptables-config" ]; then + sed -i 's|IPTABLES_MODULES=".*|IPTABLES_MODULES=""|' $container_rootfs/etc/sysconfig/iptables-config + sed -i 's|IPTABLES_MODULES_UNLOAD=".*|IPTABLES_MODULES_UNLOAD="no"|' $container_rootfs/etc/sysconfig/iptables-config + fi + + # disable readahead in the container + if [ $container_release_major = "6" -a -e $container_rootfs/etc/sysconfig/readahead ]; then + rm -f $container_rootfs/etc/init/readahead-collector.conf + rm -f $container_rootfs/etc/init/readahead-disable-services.conf + sed -i 's|READAHEAD="yes"|READAHEAD="no"|' $container_rootfs/etc/sysconfig/readahead + fi + + if [ $container_release_major = "4" ]; then + # enable fastboot always + sed -i 's|\[ -f /fastboot \]|/bin/true|' $container_rootfs/etc/rc.sysinit + sed -i 's|\[ -f /fastboot \]|/bin/true|' $container_rootfs/etc/rc.d/rc.sysinit + + # dont attempt to set kernel parameters + sed -i 's|action $"Configuring kernel parameters|# LXC action $"Configuring kernel parameters|' $container_rootfs/etc/rc.sysinit + sed -i 's|action $"Configuring kernel parameters|# LXC action $"Configuring kernel parameters|' $container_rootfs/etc/rc.d/rc.sysinit + sed -i 's|action $"Setting network parameters|# LXC action $"Setting network parameters|' $container_rootfs/etc/init.d/network 2>/dev/null + sed -i 's|action $"Setting network parameters|# LXC action $"Setting network parameters|' $container_rootfs/etc/init.d/NetworkManager 2>/dev/null + fi + + # no need to attempt to mount / + sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.sysinit + sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.d/rc.sysinit + sed -i 's|action \$"Remounting root filesystem|/bin/true # LXC action $"Remounting root filesystem|' $container_rootfs/etc/rc.sysinit + sed -i 's|action \$"Remounting root filesystem|/bin/true # LXC action $"Remounting root filesystem|' $container_rootfs/etc/rc.d/rc.sysinit + + # disable udev in the container + if [ $container_release_major = "4" ]; then + sed -i 's|\[ -x /sbin/start_udev \]|# LXC no udev|' $container_rootfs/etc/rc.sysinit + sed -i 's|\[ -x /sbin/start_udev \]|# LXC no udev|' $container_rootfs/etc/rc.d/rc.sysinit + else + sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.sysinit + sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.d/rc.sysinit + fi + + # disable nash raidautorun in the container since no /dev/md* + if [ $container_release_major = "4" -o $container_release_major = "5" ]; then + sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.sysinit + sed -i 's|echo "raidautorun /dev/md0"|echo ""|' $container_rootfs/etc/rc.d/rc.sysinit + fi + + # prevent rc.sysinit from attempting to loadkeys + if [ \( $container_release_major = "4" -o $container_release_major = "5" \) -a -e $container_rootfs/etc/sysconfig/keyboard ]; then + rm $container_rootfs/etc/sysconfig/keyboard + fi + + # dont use the hwclock, it messes up the host's time + if [ $container_release_major = "4" ]; then + sed -i 's|runcmd $"Syncing hardware clock|# LXC no hwclock runcmd $"Syncing hardware clock|' $container_rootfs/etc/rc.d/init.d/halt + else + sed -i 's|\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.d/init.d/halt + fi + sed -i 's|^\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.sysinit + sed -i 's|^\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.d/rc.sysinit + sed -i 's|^/sbin/hwclock|# LXC /sbin/nohwclock|' $container_rootfs/etc/rc.sysinit + sed -i 's|^/sbin/hwclock|# LXC /sbin/nohwclock|' $container_rootfs/etc/rc.d/rc.sysinit + + # dont start lvm + if [ $container_release_major -lt "6" -a -f $container_rootfs/sbin/lvm.static ]; then + mv $container_rootfs/sbin/lvm.static $container_rootfs/sbin/lvm.static.lxc-disabled + fi + if [ $container_release_major = "6" ]; then + touch $container_rootfs/.nolvm + fi + + # fix assumptions that plymouth is available + sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.sysinit + sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.d/rc.sysinit + rm -f $container_rootfs/etc/init/plymouth-shutdown.conf + rm -f $container_rootfs/etc/init/quit-plymouth.conf + rm -f $container_rootfs/etc/init/splash-manager.conf + + # dont try to unmount /dev/lxc devices + sed -i 's|&& $1 !~ /^\\/dev\\/ram/|\&\& $2 !~ /^\\/dev\\/lxc/ \&\& $1 !~ /^\\/dev\\/ram/|' $container_rootfs/etc/init.d/halt + + # don't try to unmount swap + sed -i 's|\[ -f /proc/swaps \]|# LXC [ -f /proc/swaps ]|' $container_rootfs/etc/init.d/halt + + # sem_open(3) checks that /dev/shm is SHMFS_SUPER_MAGIC, so make sure to + # mount /dev/shm (normally done by dracut initrd) as tmpfs + if [ $container_release_major = "4" -o $container_release_major = "5" ]; then + grep -q "mount -t tmpfs tmpfs /dev/shm" $container_rootfs/etc/rc.sysinit + if [ $? -eq 1 ]; then + echo "mkdir -p /dev/shm && mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.sysinit + echo "mkdir -p /dev/shm && mount -t tmpfs tmpfs /dev/shm" >>$container_rootfs/etc/rc.d/rc.sysinit + fi + fi + if [ $container_release_major = "6" ]; then + sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mkdir -p /dev/shm \&\& mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.sysinit + sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mkdir -p /dev/shm \&\& mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.d/rc.sysinit + fi + + # there might be other services that are useless but the below set is a good start + # some of these might not exist in the image, so we silence chkconfig complaining + # about the service file not being found + for service in \ + acpid apmd auditd autofs cpuspeed dund gpm haldaemon hidd \ + ip6tables irqbalance iscsi iscsid isdn kdump kudzu \ + lm_sensors lvm2-monitor mdmonitor microcode_ctl \ + ntpd pcmcia postfix sendmail udev-post xfs ; + do + chroot $container_rootfs chkconfig 2>/dev/null $service off + done + + for service in rsyslog ; + do + chroot $container_rootfs chkconfig 2>/dev/null $service on + done +} + +container_rootfs_configure() +{ + container_rootfs_patch + echo "Configuring container for Oracle Linux $container_release_major.$container_release_minor" + + # configure the network to use dhcp. we set DHCP_HOSTNAME so the guest + # will report its name and be resolv'able by the hosts dnsmasq + cat < $container_rootfs/etc/sysconfig/network-scripts/ifcfg-eth0 +DEVICE=eth0 +BOOTPROTO=dhcp +ONBOOT=yes +HOSTNAME=$name +DHCP_HOSTNAME=\`hostname\` +NM_CONTROLLED=no +TYPE=Ethernet +EOF + + # set the hostname + if [ $container_release_major -ge "7" ]; then + # systemd honors /etc/hostname + echo "$name" >$container_rootfs/etc/hostname + fi + cat < $container_rootfs/etc/sysconfig/network +NETWORKING=yes +NETWORKING_IPV6=no +HOSTNAME=$name +EOF + + # set minimal hosts + echo "127.0.0.1 localhost $name" > $container_rootfs/etc/hosts + + # this file has to exist for libvirt/Virtual machine monitor to boot the container + touch $container_rootfs/etc/mtab + + # setup console and tty[1-4] for login. note that /dev/console and + # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and + # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. + # lxc will maintain these links and bind mount ptys over /dev/lxc/* + # since lxc.tty.dir is specified in the config. + + # allow root login on console, tty[1-4], and pts/0 for libvirt + echo "# LXC (Linux Containers)" >>$container_rootfs/etc/securetty + echo "lxc/console" >>$container_rootfs/etc/securetty + for i in 1 2 3 4; do + echo "lxc/tty$i" >>$container_rootfs/etc/securetty + done + echo "# For libvirt/Virtual Machine Monitor" >>$container_rootfs/etc/securetty + for i in 0 1 2 3 4; do + echo "pts/$i" >>$container_rootfs/etc/securetty + done + + # prevent mingetty from calling vhangup(2) since it fails with userns + if [ -f $container_rootfs/etc/init/tty.conf ]; then + sed -i 's|mingetty|mingetty --nohangup|' $container_rootfs/etc/init/tty.conf + fi + + # create maygetty which only spawns a getty on the console when running + # under lxc, not libvirt-lxc which symlinks /dev/console to the same pty + # as /dev/tty1 + cat <$container_rootfs/sbin/maygetty +#!/bin/sh +if [ "\$container" = "lxc" ]; then + exec /sbin/mingetty \$@ +fi +exec sleep infinity +EOF + chmod 755 $container_rootfs/sbin/maygetty + + # start a getty on /dev/console, /dev/tty[1-4] + if [ $container_release_major = "4" -o $container_release_major = "5" ]; then + sed -i 's|mingetty|mingetty --nohangup|' $container_rootfs/etc/inittab + sed -i '/1:2345:respawn/i cns:2345:respawn:/sbin/maygetty --nohangup --noclear console' $container_rootfs/etc/inittab + sed -i '/5:2345:respawn/d' $container_rootfs/etc/inittab + sed -i '/6:2345:respawn/d' $container_rootfs/etc/inittab + fi + + if [ $container_release_major = "6" ]; then + cat < $container_rootfs/etc/init/console.conf +# console - getty +# +# This service maintains a getty on the console from the point the system is +# started until it is shut down again. + +start on stopped rc RUNLEVEL=[2345] +stop on runlevel [!2345] +env container + +respawn +exec /sbin/maygetty --nohangup --noclear /dev/console +EOF + fi + + # lxc-shutdown sends SIGPWR to init, OL4 and OL5 have SysVInit, just + # make it do shutdown now instead of delaying 2 minutes. OL6 uses + # upstart, so we create an upstart job to handle SIGPWR to shut down + # cleanly. We use "init 0" instead of shutdown -h now to avoid SELinux + # permission denied when upstart's shutdown tries to connect to the + # /com/ubuntu/upstart socket. + if [ $container_release_major = "4" -o $container_release_major = "5" ]; then + sed -i 's|pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; |pf::powerfail:/sbin/shutdown -f -h now "|' $container_rootfs/etc/inittab + elif [ $container_release_major = "6" ]; then + cat < $container_rootfs/etc/init/power-status-changed.conf +# power-status-changed - used to cleanly shut down the container +# +# This task is run whenever init receives SIGPWR +# Used to shut down the machine. + +start on power-status-changed + +exec init 0 +EOF + fi + + # start with a clean /var/log/messages + rm -f $container_rootfs/var/log/messages + + # set initial timezone as on host + if [ -f /etc/sysconfig/clock ]; then + . /etc/sysconfig/clock + if [ $container_release_major = "5" -o $container_release_major = "6" ]; then + echo ZONE=$ZONE > $container_rootfs/etc/sysconfig/clock + chroot $container_rootfs tzdata-update + else + ZONE="${ZONE// /_}" + chroot $container_rootfs ln -sf ../usr/share/zoneinfo/$ZONE /etc/localtime + fi + else + ZONE=`readlink /etc/localtime | sed -s "s/\.\.\/usr\/share\/zoneinfo\///g"` + if [ "$ZONE" ]; then + if [ $container_release_major = "5" -o $container_release_major = "6" ]; then + echo ZONE=$ZONE > $container_rootfs/etc/sysconfig/clock + chroot $container_rootfs tzdata-update + else + # if /etc/localtime is a symlink, this should preserve it. + cp -a /etc/localtime $container_rootfs/etc/localtime + fi + else + echo "Timezone in container is not configured. Adjust it manually." + fi + fi + + # add oracle user + chroot $container_rootfs useradd -m -s /bin/bash oracle + printf "Added container user:\033[1moracle\033[0m\n" + printf "Added container user:\033[1mroot\033[0m\n" +} + +# create the container's lxc config file +container_config_create() +{ + echo "Create configuration file $cfg_dir/config" + mkdir -p $cfg_dir || die "unable to create config dir $cfg_dir" + + echo "# Common configuration" >> $cfg_dir/config + if [ -e "@LXCTEMPLATECONFIG@/oracle.common.conf" ]; then + echo "lxc.include = @LXCTEMPLATECONFIG@/oracle.common.conf" >> $cfg_dir/config + fi + + cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" +# Container configuration for Oracle Linux $container_release_major.$container_release_minor +lxc.arch = $arch +lxc.uts.name = $name +EOF + grep -q "^lxc.rootfs.path" $cfg_dir/config 2>/dev/null || echo "lxc.rootfs.path = $container_rootfs" >> $cfg_dir/config + + if [ $container_release_major != "4" ]; then + echo "lxc.cap.drop = sys_resource" >>$cfg_dir/config + fi + + # systemd services like logind and journald need these + if [ $container_release_major != "7" ]; then + echo "lxc.cap.drop = setfcap setpcap" >>$cfg_dir/config + fi + + echo "# Networking" >>$cfg_dir/config + # see if the default network settings were already specified + lxc_network_type=`grep '^lxc.net.0.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + if [ -z "$lxc_network_type" ]; then + echo "lxc.net.0.type = veth" >>$cfg_dir/config + lxc_network_type=veth + fi + + lxc_network_link=`grep '^lxc.net.0.link' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + if [ -z "$lxc_network_link" ]; then + echo "lxc.net.0.link = lxcbr0" >>$cfg_dir/config + lxc_network_link=lxcbr0 + fi + + lxc_network_hwaddr=`grep '^lxc.net.0.hwaddr' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + if [ -z "$lxc_network_hwaddr" ]; then + # generate a hwaddr for the container + # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 + local hwaddr="00:16:3e:`dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ + head -n1 | awk '{print $2}' | cut -c1-6 | \ + sed 's/\(..\)/\1:/g; s/.$//'`" + echo "lxc.net.0.hwaddr = $hwaddr" >>$cfg_dir/config + fi + + lxc_network_flags=`grep '^lxc.net.0.flags' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + if [ -z "$lxc_network_flags" ]; then + echo "lxc.net.0.flags = up" >>$cfg_dir/config + fi + + cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" +lxc.net.0.name = eth0 +lxc.net.0.mtu = 1500 +EOF +} + +container_rootfs_clone() +{ + if is_btrfs_subvolume $template_rootfs; then + # lxc-create already made $container_rootfs a btrfs subvolume, but + # in this case we want to snapshot the original subvolume so we we + # have to delete the one that lxc-create made + btrfs subvolume delete $container_rootfs + btrfs subvolume snapshot $template_rootfs $container_rootfs || die "btrfs clone template" + else + echo "Copying rootfs ..." + cp -axT $template_rootfs $container_rootfs || die "copy template" + fi +} + +container_rootfs_repo_create() +{ + echo "# LXC generated .repo file" >$1 + echo "[$2]" >>$1 + echo "name=Oracle Linux $container_release_major.$container_release_minor ($basearch)" >>$1 + echo "baseurl=$3/" >>$1 + echo "enabled=1" >>$1 + echo "skip_if_unavailable=1" >>$1 + + if [ "$4" != "" ]; then + echo "gpgkey=$yum_url/RPM-GPG-KEY-oracle-ol$container_release_major" >>$1 + echo "gpgcheck=1" >>$1 + else + echo "gpgcheck=0" >>$1 + fi +} + +container_rootfs_dev_create() +{ + # create required devices. note that /dev/console will be created by lxc + # or libvirt itself to be a symlink to the right pty. + # take care to not nuke /dev in case $container_rootfs isn't set + dev_path="$container_rootfs/dev" + if [ $container_rootfs != "/" -a -d $dev_path ]; then + rm -rf $dev_path + fi + mkdir -p $dev_path + if can_chcon; then + # ensure symlinks created in /dev have the right context + chcon -t device_t $dev_path + fi + mknod -m 666 $dev_path/null c 1 3 + mknod -m 666 $dev_path/zero c 1 5 + mknod -m 666 $dev_path/random c 1 8 + mknod -m 666 $dev_path/urandom c 1 9 + mkdir -m 755 $dev_path/pts + mkdir -m 1777 $dev_path/shm + mknod -m 666 $dev_path/tty c 5 0 + mknod -m 666 $dev_path/tty1 c 4 1 + mknod -m 666 $dev_path/tty2 c 4 2 + mknod -m 666 $dev_path/tty3 c 4 3 + mknod -m 666 $dev_path/tty4 c 4 4 + mknod -m 666 $dev_path/full c 1 7 + mknod -m 600 $dev_path/initctl p + + # set selinux labels same as host + if can_chcon; then + for node in null zero random urandom pts shm \ + tty tty0 tty1 tty2 tty3 tty4 full ; + do + chcon --reference /dev/$node $dev_path/$node 2>/dev/null + done + fi +} + +container_rootfs_create() +{ + if can_chcon; then + chcon --reference / $container_rootfs 2>/dev/null + fi + + cmds="rpm wget yum" + if [ $container_release_major -lt "6" ]; then + if [ $host_distribution = "Ubuntu" -o $host_distribution = "Debian" ]; then + db_dump_cmd="db5.1_dump" + fi + if [ $host_distribution = "OracleServer" -o \ + $host_distribution = "Fedora" ]; then + db_dump_cmd="db_dump" + fi + + cmds="$cmds $db_dump_cmd file" + fi + for cmd in $cmds; do + which $cmd >/dev/null 2>&1 + if [ $? -ne 0 ]; then + die "The $cmd command is required, please install it" + fi + done + + mkdir -p @LOCALSTATEDIR@/lock/subsys + ( + flock -x 9 + if [ $? -ne 0 ]; then + die "The template is busy." + fi + + echo "Yum installing release $container_release_major.$container_release_minor for $basearch" + + if [ -n "$repourl" ]; then + yum_url=$repourl + else + yum_url=http://public-yum.oracle.com + fi + if [ $container_release_major = "4" -o $container_release_major = "5" ]; then + latest_L="el" + latest_U="EL" + else + latest_L="ol" + latest_U="OL" + fi + + if [ -n "$baseurl" ]; then + # create .repo pointing at baseurl + repo="lxc-install" + mkdir -p $container_rootfs/etc/yum.repos.d + container_rootfs_repo_create \ + $container_rootfs/etc/yum.repos.d/lxc-install.repo $repo $baseurl + else + # get public-yum repo file + if [ $container_release_major = "4" ]; then + repofile=public-yum-el4.repo + elif [ $container_release_major = "5" ]; then + repofile=public-yum-el5.repo + elif [ $container_release_major = "6" ]; then + repofile=public-yum-ol6.repo + elif [ $container_release_major = "7" ]; then + repofile=public-yum-ol7.repo + else + die "Unsupported release $container_release_major" + fi + + mkdir -p $container_rootfs/etc/yum.repos.d + wget -q $yum_url/$repofile -O $container_rootfs/etc/yum.repos.d/$repofile + if [ $? -ne 0 ]; then + die "Unable to download repo file $yum_url/$repofile, release unavailable" + fi + + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" $container_rootfs/etc/yum.repos.d/$repofile + + # replace url if they specified one + if [ -n "$repourl" ]; then + sed -i "s|baseurl=http://public-yum.oracle.com/repo|baseurl=$repourl/repo|" $container_rootfs/etc/yum.repos.d/$repofile + sed -i "s|gpgkey=http://public-yum.oracle.com|gpgkey=$repourl|" $container_rootfs/etc/yum.repos.d/$repofile + fi + + # disable all repos, then enable the repo for the version we are installing. + if [ $container_release_minor = "latest" ]; then + repo=$latest_L""$container_release_major"_"$container_release_minor + elif [ $container_release_major = "7" ]; then + repo="ol"$container_release_major"_u"$container_release_minor"_base" + elif [ $container_release_major = "6" ]; then + if [ $container_release_minor = "0" ]; then + repo="ol"$container_release_major"_ga_base" + else + repo="ol"$container_release_major"_u"$container_release_minor"_base" + fi + elif [ $container_release_major = "5" ]; then + if [ $container_release_minor = "0" ]; then + repo="el"$container_release_major"_ga_base" + elif [ $container_release_minor -lt "6" ]; then + repo="el"$container_release_major"_u"$container_release_minor"_base" + else + repo="ol"$container_release_major"_u"$container_release_minor"_base" + fi + elif [ $container_release_major = "4" -a $container_release_minor -gt "5" ]; then + repo="el"$container_release_major"_u"$container_release_minor"_base" + else + die "Unsupported release $container_release_major.$container_release_minor" + fi + sed -i "s|enabled=1|enabled=0|" $container_rootfs/etc/yum.repos.d/$repofile + sed -i "/\[$repo\]/,/\[/ s/enabled=0/enabled=1/" $container_rootfs/etc/yum.repos.d/$repofile + fi + + container_rootfs_dev_create + + # don't put devpts,proc, nor sysfs in here, it will already be mounted for us by lxc/libvirt + echo "" >$container_rootfs/etc/fstab + + # create rpm db, download and yum install minimal packages + mkdir -p $container_rootfs/var/lib/rpm + rpm --root $container_rootfs --initdb + yum_args="--installroot $container_rootfs --disablerepo=* --enablerepo=$repo -y --nogpgcheck" + min_pkgs="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils oraclelinux-release" + if [ $container_release_major -lt "6" ]; then + min_pkgs="$min_pkgs db4-utils" + fi + + # we unshare the mount namespace because yum installing the ol4 + # packages causes $rootfs/proc to be mounted on + lxc-unshare -s MOUNT yum -- $yum_args install $min_pkgs $user_pkgs + if [ $? -ne 0 ]; then + die "Failed to download and install the rootfs, aborting." + fi + + # rsyslog and pam depend on coreutils for some common commands in + # their POSTIN scriptlets, but coreutils wasn't installed yet. now + # that coreutils is installed, reinstall the packages so their POSTIN + # runs right. similarly, libutempter depends on libselinux.so.1 when + # it runs /usr/sbin/groupadd, so reinstall it too + redo_pkgs="" + if [ $container_release_major = "5" ]; then + if [ $container_release_minor = "latest" ]; then + redo_pkgs="pam rsyslog libutempter" + elif [ $container_release_minor -lt 2 ]; then + redo_pkgs="pam" + elif [ $container_release_minor -lt 6 ]; then + redo_pkgs="pam rsyslog" + elif [ $container_release_minor -gt 5 ]; then + redo_pkgs="pam rsyslog libutempter" + fi + fi + # shadow utils fails on ol4 and ol6.1 + if [ $container_release_major = "4" -o \ + $container_release_major = "6" -a $container_release_minor = "1" ]; then + redo_pkgs="shadow-utils" + fi + if [ x"$redo_pkgs" != x ]; then + rpm --root $container_rootfs --nodeps -e $redo_pkgs + lxc-unshare -s MOUNT yum -- $yum_args install $redo_pkgs + if [ $? -ne 0 ]; then + die "Unable to reinstall packages" + fi + fi + + # if installing from a baseurl, create a .repo that the container + # can use to update to _latest from http://public-yum.oracle.com + if [ -n "$baseurl" ]; then + container_rootfs_repo_create \ + "$container_rootfs/etc/yum.repos.d/public-yum-"$latestL""$container_release_major".repo" \ + $latest_L""$container_release_major"_latest" \ + $yum_url"/repo/OracleLinux/"$latest_U""$container_release_major"/latest/$basearch" gpg + fi + + # these distributions put the rpm database in a place the guest is + # not expecting it, so move it + if [ $host_distribution = "Ubuntu" -o $host_distribution = "Debian" ]; then + mv $container_rootfs/$HOME/.rpmdb/* $container_rootfs/var/lib/rpm + fi + + # if the native rpm created the db with Hash version 9, we need to + # downgrade it to Hash version 8 for use with OL5.x + db_version=`file $container_rootfs/var/lib/rpm/Packages | \ + grep -o 'version [0-9]*' |awk '{print $2}'` + if [ $container_release_major -lt "6" -a $db_version != "8" ]; then + echo "Fixing (downgrading) rpm database from version $db_version" + rm -f $container_rootfs/var/lib/rpm/__db* + for db in $container_rootfs/var/lib/rpm/* ; do + $db_dump_cmd $db |chroot $container_rootfs db_load /var/lib/rpm/`basename $db`.new + mv $db.new $db + done + fi + + # the host rpm may not be the same as the guest, rebuild the db with + # the guest rpm version + echo "Rebuilding rpm database" + rm -f $container_rootfs/var/lib/rpm/__db* + chroot $container_rootfs rpm --rebuilddb >/dev/null 2>&1 + + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-oracle-$name + if [ $? -ne 0 ]; then + exit 1 + fi +} + +container_release_get() +{ + if [ -f $1/etc/oracle-release ]; then + container_release_version=`cat $1/etc/oracle-release |awk '/^Oracle/ {print $5}'` + container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` + container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` + elif grep -q "Enterprise Linux AS" $1/etc/redhat-release; then + container_release_major=`cat $1/etc/redhat-release |awk '{print $7}'` + container_release_minor=`cat $1/etc/redhat-release |awk '{print $10}' |tr -d ")"` + container_release_version="$container_release_major.$container_release_minor" + elif grep -q "Enterprise Linux Server" $1/etc/redhat-release; then + container_release_version=`cat $1/etc/redhat-release |awk '{print $7}'` + container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` + container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` + else + echo "Unable to determine container release version" + exit 1 + fi +} + +usage() +{ + cat < architecture (ie. i386, x86_64) + -R|--release= release to download for the new container + --rootfs= rootfs path + -r|--rpms= additional rpms to install into container + -u|--url= replace yum repo url (ie. Oracle public-yum mirror) + --baseurl= use package repository (ie. file:///mnt) + arch and release must also be specified + -t|--templatefs= copy/clone rootfs at path instead of downloading + -P|--patch= only patch the rootfs at path for use as a container + -h|--help + +Release is of the format "major.minor", for example "5.8", "6.3", or "6.latest" +This template supports Oracle Linux releases 4.6 - 7.0 +EOF + return 0 +} + +options=$(getopt -o hp:n:a:R:r:u:t: -l help,rootfs:,path:,name:,arch:,release:,rpms:,url:,templatefs:,patch:,baseurl: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi + +eval set -- "$options" +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) cfg_dir=$2; shift 2;; + --rootfs) container_rootfs=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -R|--release) container_release_version=$2; shift 2;; + -r|--rpms) user_pkgs=$2; shift 2;; + -u|--url) repourl=$2; shift 2;; + -t|--templatefs) template_rootfs=$2; shift 2;; + --patch) patch_rootfs=$2; shift 2;; + --baseurl) baseurl=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +# make sure mandatory args are given and valid +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -n "$baseurl" ]; then + if [ "$arch" = "" -o "$container_release_version" = "" ]; then + echo "The --arch and --release must be specified when using --baseurl" + usage + exit 1 + fi +fi + +if [ "$arch" = "" ]; then + arch=$(uname -m) +fi + +if [ -n "$patch_rootfs" ]; then + container_rootfs="$patch_rootfs" + container_release_get $container_rootfs + container_rootfs_patch + exit 0 +fi + +if [ -z $name ]; then + echo "Container name must be given" + usage + exit 1 +fi + +if [ -z $cfg_dir ]; then + echo "Configuration directory must be given, check lxc-create" + usage + exit 1 +fi + +basearch=$arch +if [ "$arch" = "i686" ]; then + basearch="i386" +fi + +if [ "$arch" != "i386" -a "$arch" != "x86_64" ]; then + echo "Bad architecture given, check lxc-create" + usage + exit 1 +fi + +if which lsb_release >/dev/null 2>&1; then + host_distribution=`lsb_release --id |awk '{print $3}'` + host_release_version=`lsb_release --release |awk '{print $2}'` + host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` + host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` +else + if [ -f /etc/fedora-release ]; then + host_distribution="Fedora" + host_release_version=`cat /etc/fedora-release |awk '{print $3}'` + host_release_major=$host_release_version + host_release_minor=0 + elif [ -f /etc/oracle-release ]; then + host_distribution="OracleServer" + host_release_version=`cat /etc/oracle-release |awk '{print $5}'` + host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` + host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` + else + echo "Unable to determine host distribution, ensure lsb_release is installed" + exit 1 + fi +fi +echo "Host is $host_distribution $host_release_version" + +if [ -z "$container_rootfs" ]; then + container_rootfs="$cfg_dir/rootfs" +fi + +if [ -n "$template_rootfs" ]; then + container_release_get $template_rootfs +else + if [ -z "$container_release_version" ]; then + if [ $host_distribution = "OracleServer" ]; then + container_release_version=$host_release_version + else + echo "No release specified with -R, defaulting to 6.5" + container_release_version="6.5" + fi + fi + container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` + container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` +fi + +container_config_create +if [ -n "$template_rootfs" ]; then + container_rootfs_clone +else + container_rootfs_create +fi + +container_release_get $container_rootfs + +container_rootfs_configure + +echo "Container : $container_rootfs" +echo "Config : $cfg_dir/config" +echo "Network : eth0 ($lxc_network_type) on $lxc_network_link" diff --git a/templates/lxc-plamo.in b/templates/lxc-plamo.in new file mode 100644 index 0000000..ec442ec --- /dev/null +++ b/templates/lxc-plamo.in @@ -0,0 +1,384 @@ +#!/bin/bash -eu + +# +# template script for generating Plamo Linux container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# KATOH Yasufumi +# TAMUKI Shoichi + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# ref. https://github.com/Ponce/lxc-slackware/blob/master/lxc-slackware +# lxc-ubuntu script + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +[ -r /etc/default/lxc ] && . /etc/default/lxc + +DLSCHEME=${DLSCHEME:-"http"} +MIRRORSRV=${MIRRORSRV:-"repository.plamolinux.org"} +MIRRORPATH=${MIRRORPATH:-"/pub/linux/Plamo"} +CATEGORIES=${CATEGORIES-"00_base 01_minimum"} +EXTRACTGRS=${EXTRACTGRS-""} +IGNOREPKGS=${IGNOREPKGS-"grub kernel lilo linux_firmware microcode_ctl + cpufreqd cpufrequtils gpm ntp kmod"} +ADDONPKGS=${ADDONPKGS-"`echo contrib/Hamradio/{morse,qrq}`"} + +download_plamo() { + # check the mini plamo was not already downloaded + if ! mkdir -p $ptcache ; then + echo "Failed to create '$ptcache' directory." + return 1 + fi + # download a mini plamo into a cache + echo "Downloading Plamo-$release minimal..." + cd $ptcache + case $DLSCHEME in http) depth=2 ;; ftp) depth=3 ;; esac + rej=${IGNOREPKGS%% *} ; [ -n "$rej" ] && rej="$rej-*" + if [ `echo $IGNOREPKGS | wc -w` -gt 1 ] ; then + for p in ${IGNOREPKGS#* } ; do rej="$rej,$p-*" ; done + fi + for i in $CATEGORIES ; do + wget -nv -e robots=off -r -l $depth -nd -A .tgz,.txz -R "$rej" \ + -I $MIRRORPATH/Plamo-$release/$arch/plamo/$i \ + -X $MIRRORPATH/Plamo-$release/$arch/plamo/$i/old \ + $DLSCHEME://$MIRRORSRV$MIRRORPATH/Plamo-$release/$arch/plamo/$i + if [ $? -ne 0 ] ; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + done + for i in $EXTRACTGRS ; do + wget -nv -e robots=off -r -l $depth -nd -A .tgz,.txz -R "$rej" \ + -I $MIRRORPATH/Plamo-$release/$arch/contrib/$i \ + -X $MIRRORPATH/Plamo-$release/$arch/contrib/$i/old \ + $DLSCHEME://$MIRRORSRV$MIRRORPATH/Plamo-$release/$arch/contrib/$i + if [ $? -ne 0 ] ; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + done + for p in $ADDONPKGS ; do + wget -nv -e robots=off -r -l $depth -nd -A "`basename $p`-*" \ + -I $MIRRORPATH/Plamo-$release/$arch/`dirname $p` \ + -X $MIRRORPATH/Plamo-$release/$arch/`dirname $p`/old \ + $DLSCHEME://$MIRRORSRV$MIRRORPATH/Plamo-$release/$arch/`dirname $p` + if [ $? -ne 0 ] ; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + done + mv $ptcache $dlcache + echo "Download complete." + return 0 +} + +copy_plamo() { + # make a local copy of the mini plamo + echo "Copying $rtcache to $rootfs..." + mkdir -p $rootfs + find $rtcache -mindepth 1 -maxdepth 1 -exec cp -a {} $rootfs \; || return 1 + return 0 +} + +install_plamo() { + mkdir -p @LOCALSTATEDIR@/lock/subsys + ( + if ! flock -n 9 ; then + echo "Cache repository is busy." + return 1 + fi + echo "Checking cache download in $dlcache..." + if [ ! -d $dlcache ] ; then + if ! download_plamo ; then + echo "Failed to download plamo $release base packages." + return 1 + fi + fi + # install "installpkg" command temporarily with static linked tar + # command into the lxc cache directory to keep the original uid/ + # gid of files/directories. + echo "Installing 'installpkg' command into $dlcache/sbin..." + ( cd $dlcache ; tar xpJf hdsetup-*.txz ; rm -rf tmp usr var ) + sed -i "/ldconfig/!s@/sbin@$dlcache&@g" $dlcache/sbin/installpkg* + PATH=$dlcache/sbin:$PATH + echo "Installing packages to $rtcache..." + if [ ! -d $rtcache ] ; then + mkdir -p $rtcache + for p in `ls -cr $dlcache/*.t?z` ; do + installpkg -root $rtcache -priority ADD $p + done + fi + echo "Copy $rtcache to $rootfs..." + if ! copy_plamo ; then + echo "Failed to copy rootfs." + return 1 + fi + return 0 + ) 9> @LOCALSTATEDIR@/lock/subsys/lxc-plamo +} + +configure_plamo() { + # suppress log level output for udev + sed -i 's/="err"/=0/' $rootfs/etc/udev/udev.conf + # /etc/fstab + cat <<- "EOF" > $rootfs/etc/fstab + none /proc proc defaults 0 0 + none /sys sysfs defaults 0 0 + none /dev tmpfs defaults 0 0 + none /tmp tmpfs defaults 0 0 + none /dev/pts devpts gid=5,mode=620 0 0 + none /proc/bus/usb usbfs noauto 0 0 + none /var/lib/nfs/rpc_pipefs rpc_pipefs defaults 0 0 + EOF + # /etc/inittab + cat <<- "EOF" | patch $rootfs/etc/inittab + 32,33c32,33 + < # What to do when power fails (shutdown to single user). + < pf::powerfail:/sbin/shutdown -f +5 "THE POWER IS FAILING" + --- + > # What to do when power fails (shutdown). + > pf::powerfail:/sbin/shutdown -h +0 "THE POWER IS FAILING" + 47a48 + > 1:1235:respawn:/sbin/agetty 38400 console + 52,53d52 + < c5:1235:respawn:/sbin/agetty 38400 tty5 linux + < c6:12345:respawn:/sbin/agetty 38400 tty6 linux + EOF + # set the hostname + echo "$name" > $rootfs/etc/HOSTNAME + # set minimal hosts + echo "127.0.0.1 localhost $name" > $rootfs/etc/hosts + # configure the network using the dhcp + echo "DHCP" > $rootfs/var/run/inet1-scheme + # localtime (JST) + ln -s ../usr/share/zoneinfo/Asia/Tokyo $rootfs/etc/localtime + # disable pam_loginuid.so in /etc/pam.d/login (for libvirt's lxc driver) + sed -i '/pam_loginuid/s/^/#/' $rootfs/etc/pam.d/login + # glibc configure + mv $rootfs/etc/ld.so.conf{.new,} + chroot $rootfs ldconfig + + # delete unnecessary process from rc.S + ed - $rootfs/etc/rc.d/rc.S <<- "EOF" + /^mount -w -n -t proc/;/^mkdir \/dev\/shm/-1d + /^mknod \/dev\/null/;/^# Clean \/etc\/mtab/-2d + /^# copy the rules/;/^# Set the hostname/-1d + /^# Check the integrity/;/^# Clean up temporary/-1d + w + EOF + + # delete unnecessary process from rc.M + ed - $rootfs/etc/rc.d/rc.M <<- "EOF" + /^# Screen blanks/;/^# Initialize ip6tables/-1d + /^# Initialize sysctl/;/^echo "Starting services/-1d + /^sync/;/^# All done/-1d + w + EOF + + # delete unnecessary process from rc.6 + ed - $rootfs/etc/rc.d/rc.6 <<- "EOF" + /^# Save system time/;/^# Unmount any remote filesystems/-1d + /^# Turn off swap/;/^# See if this is a powerfail situation/-1d + w + EOF + + # /etc/rc.d/rc.inet1.tradnet + head -n-93 $rootfs/sbin/netconfig.tradnet > /tmp/netconfig.rconly + cat <<- EOF >> /tmp/netconfig.rconly + PCMCIA=n + RC=$rootfs/etc/rc.d/rc.inet1.tradnet + IFCONFIG=sbin/ifconfig + ROUTE=sbin/route + INET1SCHEME=var/run/inet1-scheme + IPADDR=127.0.0.1 + NETWORK=127.0.0.0 + DHCPCD=usr/sbin/dhclient + LOOPBACK=y + make_config_file + EOF + rm -f $rootfs/etc/rc.d/rc.inet1.tradnet + sh /tmp/netconfig.rconly + rm -f /tmp/netconfig.rconly + sed -i '/cmdline/s/if/& false \&\&/' $rootfs/etc/rc.d/rc.inet1.tradnet + # /etc/rc.d/rc.inet2 + sed -i '/rpc.mountd/s/^/#/' $rootfs/etc/rc.d/rc.inet2 + sed -i '/modprobe/s/^/#/' $rootfs/etc/rc.d/rc.inet2 + # configure to start only the minimum of service + chmod 644 $rootfs/etc/rc.d/init.d/saslauthd + chmod 644 $rootfs/etc/rc.d/init.d/open-iscsi + rm -f $rootfs/etc/rc.d/init.d/postfix + rm -f $rootfs/var/log/initpkg/shadow + return 0 +} + +copy_configuration() { + ret=0 + cat <<- EOF >> $path/config || let ret++ + lxc.uts.name = $name + lxc.arch = $arch + EOF + if [ -f "@LXCTEMPLATECONFIG@/plamo.common.conf" ] ; then + cat <<- "EOF" >> $path/config || let ret++ + + lxc.include = @LXCTEMPLATECONFIG@/plamo.common.conf + EOF + fi + if [ $ret -ne 0 ] ; then + echo "Failed to add configuration." + return 1 + fi + return 0 +} + +post_process() { + # nothing do in Plamo Linux + true +} + +do_bindhome() { + # bind-mount the user's path into the container's /home + h=`getent passwd $bindhome | cut -d: -f6` + mkdir -p $rootfs/$h + echo "lxc.mount.entry = $h $rootfs/$h none bind 0 0" >> $path/config + # copy /etc/passwd, /etc/shadow, and /etc/group entries into container + if ! pwd=`getent passwd $bindhome` ; then + echo "Warning: failed to copy password entry for $bindhome." + else + echo $pwd >> $rootfs/etc/passwd + fi + echo `getent shadow $bindhome` >> $rootfs/etc/shadow +} + +cleanup() { + [ -d $dlcache -a -d $rtcache ] || return 0 + # lock, so we won't purge while someone is creating a repository + ( + if ! flock -n 9 ; then + echo "Cache repository is busy." + return 1 + fi + echo "Purging the download cache..." + rm -rf --one-file-system $dlcache $rtcache || return 1 + echo "Done." + return 0 + ) 9> @LOCALSTATEDIR@/lock/subsys/lxc-plamo +} + +usage() { + cat <<- EOF + $prog [-h|--help] -p|--path= -n|--name= --rootfs= + [-c|--clean] [-r|--release=] [-a|--arch=] + [-b|--bindhome=] + release: $release + arch: x86 or x86_64: defaults to host arch + bindhome: bind 's home into the container + EOF +} + +prog=`basename $0` +path="" ; name="" ; rootfs="" +clean=0 +release=${release:-6.x} +arch=`uname -m | sed 's/i.86/x86/'` ; hostarch=$arch +bindhome="" +sopts=hp:n:cr:a:b: +lopts=help,path:,name:,rootfs:,clean,release:,arch:,bindhome: +if ! options=`getopt -o $sopts -l $lopts -- "$@"` ; then + usage + exit 1 +fi +eval set -- "$options" +while true ; do + case "$1" in + -h|--help) usage && exit 0 ;; + -p|--path) path=$2 ; shift 2 ;; + -n|--name) name=$2 ; shift 2 ;; + --rootfs) rootfs=$2 ; shift 2 ;; + -c|--clean) clean=1 ; shift 1 ;; + -r|--release) release=$2 ; shift 2 ;; + -a|--arch) arch=$2 ; shift 2 ;; + -b|--bindhome) bindhome=$2 ; shift 2 ;; + --) shift 1 ; break ;; + *) break ;; + esac +done +if [ $clean -eq 1 -a -z "$path" ] ; then + cleanup || exit 1 + exit 0 +fi +if [ $hostarch == "x86" -a $arch == "x86_64" ] ; then + echo "Can't create x86_64 container on x86." + exit 1 +fi +if [ -z "$path" ] ; then + echo "'path' parameter is required." + exit 1 +fi +if [ -z "$name" ] ; then + echo "'name' parameter is required." + exit 1 +fi +if [ `id -u` -ne 0 ] ; then + echo "This script should be run as 'root'." + exit 1 +fi +cache="${LXC_CACHE_PATH:-@LOCALSTATEDIR@/cache/lxc}" +ptcache=$cache/partial-${prog##*-}-$release-$arch +dlcache=$cache/cache-${prog##*-}-$release-$arch +rtcache=$cache/rootfs-${prog##*-}-$release-$arch +if [ -z "$rootfs" ] ; then + if grep -q "^lxc.rootfs.path" $path/config ; then + rootfs=`awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config` + else + rootfs=$path/rootfs + fi +fi +if ! install_plamo ; then + echo "Failed to install plamo $release." + exit 1 +fi +if ! configure_plamo ; then + echo "Failed to configure plamo $release for a container." + exit 1 +fi +if ! copy_configuration ; then + echo "Failed to write configuration file." + exit 1 +fi +post_process +if [ -n "$bindhome" ] ; then + do_bindhome +fi +if [ $clean -eq 1 ] ; then + cleanup || exit 1 + exit 0 +fi diff --git a/templates/lxc-pld.in b/templates/lxc-pld.in new file mode 100644 index 0000000..b748cb8 --- /dev/null +++ b/templates/lxc-pld.in @@ -0,0 +1,484 @@ +#!/bin/sh + +# +# template script for generating PLD Linux container for LXC +# + +# +# lxc: Linux Container library + +# Authors: +# Elan Ruusamäe + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Configuration +arch=$(uname -m) +cache_base=@LOCALSTATEDIR@/cache/lxc/pld/$arch +default_path=@LXCPATH@ + +if [ -e /etc/os-release ]; then + # This is a shell friendly configuration file. We can just source it. + # What we're looking for in here is the ID, VERSION_ID and the CPE_NAME + . /etc/os-release + echo "Host CPE ID from /etc/os-release: ${CPE_NAME}" +fi + +if [ "${CPE_NAME}" != "" -a "${ID}" = "pld" -a "${VERSION_ID}" != "" ]; then + pld_host_ver=${VERSION_ID} + is_pld=true +elif [ -e /etc/pld-release ]; then + # Only if all other methods fail, try to parse the pld-release file. + pld_host_ver=$(sed -e '/PLD /!d' -e 's/^\([0-9.]*\)\sPLD.*/\1/' < /etc/pld-release) + if [ "$pld_host_ver" != "" ]; then + is_pld=true + fi +fi + +# Map a few architectures to their generic PLD Linux repository archs. +case "$pld_host_ver:$arch" in +3.0:i586) arch=i486 ;; +esac + +configure_pld() +{ + + # disable selinux + mkdir -p $rootfs_path/selinux + echo 0 > $rootfs_path/selinux/enforce + + # configure the network using the dhcp + sed -i -e "s/^HOSTNAME=.*/HOSTNAME=${utsname}/" ${rootfs_path}/etc/sysconfig/network + + # set hostname on systemd + if [ $release = "3.0" ]; then + echo "${utsname}" > ${rootfs_path}/etc/hostname + fi + + # set minimal hosts + test -e $rootfs_path/etc/hosts || \ + cat < $rootfs_path/etc/hosts +127.0.0.1 localhost.localdomain localhost $utsname +::1 localhost6.localdomain6 localhost6 +EOF + + dev_path="${rootfs_path}/dev" + rm -rf $dev_path + mkdir -p $dev_path + mknod -m 666 ${dev_path}/null c 1 3 + mknod -m 666 ${dev_path}/zero c 1 5 + mknod -m 666 ${dev_path}/random c 1 8 + mknod -m 666 ${dev_path}/urandom c 1 9 + mkdir -m 755 ${dev_path}/pts + mkdir -m 1777 ${dev_path}/shm + mknod -m 666 ${dev_path}/tty c 5 0 + mknod -m 666 ${dev_path}/tty0 c 4 0 + mknod -m 666 ${dev_path}/tty1 c 4 1 + mknod -m 666 ${dev_path}/tty2 c 4 2 + mknod -m 666 ${dev_path}/tty3 c 4 3 + mknod -m 666 ${dev_path}/tty4 c 4 4 + mknod -m 600 ${dev_path}/console c 5 1 + mknod -m 666 ${dev_path}/full c 1 7 + mknod -m 600 ${dev_path}/initctl p + mknod -m 666 ${dev_path}/ptmx c 5 2 + + if [ -n "${root_password}" ]; then + echo "setting root passwd to $root_password" + echo "root:$root_password" | chroot $rootfs_path chpasswd + fi + + return 0 +} + +configure_pld_init() +{ + # default powerfail action waits 2 minutes. for lxc we want it immediately + sed -i -e '/^pf::powerfail:/ s,/sbin/shutdown.*,/sbin/halt,' ${rootfs_path}/etc/inittab +} + +configure_pld_systemd() +{ + unlink ${rootfs_path}/etc/systemd/system/default.target + chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/udev.service + chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target + + # Actually, the After=dev-%i.device line does not appear in the + # Fedora 17 or Fedora 18 systemd getty@.service file. It may be left + # over from an earlier version and it's not doing any harm. We do need + # to disable the "ConditionalPathExists=/dev/tty0" line or no gettys are + # started on the ttys in the container. Lets do it in an override copy of + # the service so it can still pass rpm verifies and not be automatically + # updated by a new systemd version. -- mhw /\/\|=mhw=|\/\/ + + sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ + -e 's/After=dev-%i.device/After=/' \ + < ${rootfs_path}/lib/systemd/system/getty@.service \ + > ${rootfs_path}/etc/systemd/system/getty@.service + + # Setup getty service on the 4 ttys we are going to allow in the + # default config. Number should match lxc.tty.max + for i in 1 2 3 4; do + ln -sf ../getty@.service ${rootfs_path}/etc/systemd/system/getty.target.wants/getty@tty${i}.service + done +} + +download_pld() +{ + + # check the mini pld was not already downloaded + INSTALL_ROOT=$cache/partial + mkdir -p $INSTALL_ROOT + if [ $? -ne 0 ]; then + echo "Failed to create '$INSTALL_ROOT' directory" + return 1 + fi + + # download a mini pld into a cache + echo "Downloading PLD Linux minimal ..." + POLDEK="poldek --root $INSTALL_ROOT --noask --nohold --noignore" + PKG_LIST="basesystem filesystem pld-release rpm poldek vserver-packages rc-scripts pwdutils mingetty" + + mkdir -p $INSTALL_ROOT@LOCALSTATEDIR@/lib/rpm + rpm --root $INSTALL_ROOT --initdb + $POLDEK -u $PKG_LIST + + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + + mv "$INSTALL_ROOT" "$cache/rootfs" + echo "Download complete." + + return 0 +} + +copy_pld() +{ + + # make a local copy of the minipld + echo -n "Copying rootfs to $rootfs_path ..." + cp -a $cache/rootfs/* $rootfs_path || return 1 + return 0 +} + +update_pld() +{ + POLDEK="poldek --root $cache/rootfs --noask" + $POLDEK --upgrade-dist +} + +install_pld() +{ + mkdir -p @LOCALSTATEDIR@/lock/subsys/ + ( + flock -x 9 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + echo "Checking cache download in $cache/rootfs ... " + if [ ! -e "$cache/rootfs" ]; then + download_pld + if [ $? -ne 0 ]; then + echo "Failed to download 'pld base'" + return 1 + fi + else + echo "Cache found. Updating..." + update_pld + if [ $? -ne 0 ]; then + echo "Failed to update 'pld base', continuing with last known good cache" + else + echo "Update finished" + fi + fi + + echo "Copy $cache/rootfs to $rootfs_path ... " + copy_pld + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + + return 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-pld + + return $? +} + +copy_configuration() +{ + + mkdir -p $config_path + grep -q "^lxc.rootfs.path" $config_path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs_path" >> $config_path/config + cat <> $config_path/config +# Most of below settings should be taken as defaults from +# lxc.include = /usr/share/lxc/config/common.conf +lxc.uts.name = $utsname +lxc.tty.max = 4 +lxc.pty.max = 1024 +# Consider if below line is right for systemd container +lxc.mount.fstab = $config_path/fstab +lxc.cap.drop = sys_module mac_admin mac_override sys_time + +lxc.autodev = $auto_dev + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +## Devices +# Allow all devices +#lxc.cgroup.devices.allow = a +# Deny all devices +lxc.cgroup.devices.deny = a +# Allow to mknod all devices (but not using them) +lxc.cgroup.devices.allow = c *:* m +lxc.cgroup.devices.allow = b *:* m + +# /dev/null and zero +lxc.cgroup.devices.allow = c 1:3 rwm +lxc.cgroup.devices.allow = c 1:5 rwm +# consoles +lxc.cgroup.devices.allow = c 5:1 rwm +lxc.cgroup.devices.allow = c 5:0 rwm +lxc.cgroup.devices.allow = c 4:0 rwm +lxc.cgroup.devices.allow = c 4:1 rwm +# /dev/{,u}random +lxc.cgroup.devices.allow = c 1:9 rwm +lxc.cgroup.devices.allow = c 1:8 rwm +lxc.cgroup.devices.allow = c 136:* rwm +lxc.cgroup.devices.allow = c 5:2 rwm +# rtc +lxc.cgroup.devices.allow = c 254:0 rm +EOF + + cat < $config_path/fstab +proc proc proc nodev,noexec,nosuid 0 0 +sysfs sys sysfs defaults 0 0 +EOF + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +clean() +{ + + if [ ! -e $cache ]; then + exit 0 + fi + + # lock, so we won't purge while someone is creating a repository + ( + flock -x 9 + if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 + fi + + echo -n "Purging the download cache for PLD Linux $release..." + rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 + exit 0 + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-pld +} + +usage() +{ + cat < + [-p|--path=] [-c|--clean] [-R|--release=] [--fqdn=] [-A|--arch=] + [-h|--help] +Mandatory args: + -n,--name container name, used to as an identifier for that container from now on +Optional args: + -p,--path path to where the container will be created, defaults to @LXCPATH@. The container config will go under @LXCPATH@ in that case + --rootfs path for actual rootfs. + -c,--clean clean the cache + -R,--release PLD Linux release for the new container. if the host is PLD Linux, then it will default to the host's release. + --fqdn fully qualified domain name (FQDN) for DNS and system naming + -A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64] + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o hp:n:cR: -l help,path:,rootfs:,name:,clean,release:,fqdn: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +while :; do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -c|--clean) clean=$2; shift 2;; + -R|--release) release=$2; shift 2;; + --fqdn) utsname=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ ! -z "$clean" -a -z "$path" ]; then + clean || exit 1 + exit 0 +fi + +if [ -z "${utsname}" ]; then + utsname=${name} +fi + +# This follows a standard "resolver" convention that an FQDN must have +# at least two dots or it is considered a local relative host name. +# If it doesn't, append the dns domain name of the host system. +# +# This changes one significant behavior when running +# "lxc_create -n Container_Name" without using the +# --fqdn option. +# +# Old behavior: +# utsname and hostname = Container_Name +# New behavior: +# utsname and hostname = Container_Name.Domain_Name + +if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then + if [ -n "$(dnsdomainname)" ]; then + utsname=${utsname}.$(dnsdomainname) + fi +fi + +needed_pkgs="" +type poldek >/dev/null 2>&1 +if [ $? -ne 0 ]; then + needed_pkgs="poldek $needed_pkgs" +fi + +#type curl >/dev/null 2>&1 +#if [ $? -ne 0 ]; then +# needed_pkgs="curl $needed_pkgs" +#fi + +if [ -n "$needed_pkgs" ]; then + echo "Missing commands: $needed_pkgs" + echo "Please install these using \"sudo poldek -u $needed_pkgs\"" + exit 1 +fi + +if [ -z "$path" ]; then + path=$default_path/$name +fi + +if [ -z "$release" ]; then + if [ "$is_pld" -a "$pld_host_ver" ]; then + release=$pld_host_ver + else + echo "This is not a PLD Linux host and release missing, defaulting to 3.0. use -R|--release to specify release" + release=3.0 + fi +fi + +# pld th have systemd. We need autodev enabled to keep systemd from causing problems. +if [ $release = 3.0 ]; then + auto_dev="0" +else + auto_dev="0" +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -z "$rootfs" ]; then + rootfs_path=$path/rootfs + # check for 'lxc.rootfs.path' passed in through default config by lxc-create + # TODO: should be lxc.rootfs.mount used instead? + if grep -q '^lxc.rootfs.path' $path/config 2>/dev/null ; then + rootfs_path=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $path/config) + fi +else + rootfs_path=$rootfs +fi +config_path=$default_path/$name +cache=$cache_base/$release + +revert() +{ + echo "Interrupted, so cleaning up" + lxc-destroy -n $name + # maybe was interrupted before copy config + rm -rf $path + rm -rf $default_path/$name + echo "exiting..." + exit 1 +} + +trap revert SIGHUP SIGINT SIGTERM + +copy_configuration +if [ $? -ne 0 ]; then + echo "Failed write configuration file" + exit 1 +fi + +install_pld +if [ $? -ne 0 ]; then + echo "Failed to install PLD Linux" + exit 1 +fi + +configure_pld +if [ $? -ne 0 ]; then + echo "Failed to configure PLD Linux for a container" + exit 1 +fi + +# If the systemd configuration directory exists - set it up for what we need. +if [ -d ${rootfs_path}/etc/systemd/system ]; then + configure_pld_systemd +fi + +# This configuration (rc.sysinit) is not inconsistent with the systemd stuff +# above and may actually coexist on some upgraded systems. Let's just make +# sure that, if it exists, we update this file, even if it's not used... +if [ -f ${rootfs_path}/etc/rc.sysinit ]; then + configure_pld_init +fi + +if [ ! -z $clean ]; then + clean || exit 1 + exit 0 +fi +echo "container rootfs and config created" diff --git a/templates/lxc-sabayon.in b/templates/lxc-sabayon.in new file mode 100644 index 0000000..51594c1 --- /dev/null +++ b/templates/lxc-sabayon.in @@ -0,0 +1,513 @@ +#!/bin/bash +# vim: set ts=4 sw=4 expandtab + +# Exit on error and treat unset variables as an error. +set -eu + +# +# LXC template for Sabayon OS, based of Alpine script. +# + +# Authors: +# Geaaru + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +#=========================== Constants ============================# + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +readonly LOCAL_STATE_DIR='@LOCALSTATEDIR@' +readonly LXC_TEMPLATE_CONFIG='@LXCTEMPLATECONFIG@' + + +# Temporary static MIRROR LIST. I will get list from online path on the near future. +readonly MIRRORS_LIST=" +http://mirror.it.sabayon.org/ +http://dl.sabayon.org/ +ftp://ftp.klid.dk/sabayonlinux/ +http://ftp.fsn.hu/pub/linux/distributions/sabayon/ +http://ftp.cc.uoc.gr/mirrors/linux/SabayonLinux/ +http://ftp.rnl.ist.utl.pt/pub/sabayon/ +ftp://ftp.nluug.nl/pub/os/Linux/distr/sabayonlinux/ +http://mirror.internode.on.net/pub/sabayon/ +http://mirror.yandex.ru/sabayon/ +http://sabayon.c3sl.ufpr.br/ +http://mirror.clarkson.edu/sabayon/ +http://na.mirror.garr.it/mirrors/sabayonlinux/" + +#======================== Global variables ========================# + +# Clean variables and set defaults. +arch="$(uname -m)" +debug='no' +flush_cache='no' +mirror_url= +name= +path= +release="DAILY" +rootfs= +unprivileged=false +mapped_uid= +mapped_gid= +flush_owner=false + +#======================== Helper Functions ========================# + +usage() { + cat <<-EOF +Template specific options can be passed to lxc-create after a '--' like this: + + lxc-create --name=NAME [lxc-create-options] -- [template-options] + +Template options: + -a ARCH, --arch=ARCH The container architecture (e.g. x86_64, armv7); defaults + to the host arch. + -d, --debug Run this script in a debug mode (set -x and wget w/o -q). + -m URL --mirror=URL The Sabayon mirror to use; defaults to random mirror. + -u, --unprivileged Tuning of rootfs for unprivileged containers. + -r, --release Identify release to use. Default is DAILY. + --mapped-gid Group Id to use on unprivileged container + (based of value present on file /etc/subgid). + --mapped-uid User Id to use on unprivileged container + (based of value present on file /etc/subuid) + --flush-owner Only for directly creation of unprivileged containers + through lxc-create command. Execute fuidshift command. + Require --mapped-gid,--mapped-uid and --unprivileged + options. + +Environment variables: + RELEASE Release version of Sabayon. Default is ${RELEASE}. +EOF +} + +random_mirror_url() { + local url="" + + if [ $arch == 'amd64' ] ; then + url=$(echo $MIRRORS_LIST | sed -e 's/ /\n/g' | sort -R --random-source=/dev/urandom | head -n 1) + else + if [ $arch == 'armv7l' ] ; then + # Currently armv7l tarball is not on sabayon mirrored tree. + url="https://dockerbuilder.sabayon.org/" + fi + fi + + [ -n "$url" ] && echo "$url" +} + +die() { + local retval=$1; shift + + echo -e "==> $@\n" + exit $retval +} + +einfo() { + echo -e "==> $@\n" +} + +fetch() { + if [ "$debug" = 'yes' ]; then + wget -T 10 -O - $@ + else + wget -T 10 -O - -q $@ + fi +} + +parse_arch() { + case "$1" in + x86_64 | amd64) echo 'amd64';; + armv7 | armv7l) echo 'armv7l';; + #arm*) echo 'armhf';; + *) return 1;; + esac +} + +run_exclusively() { + + local lock_name="$1" + local timeout=$2 + local method=$3 + shift 3 + + mkdir -p "$LOCAL_STATE_DIR/lock/subsys" + + local retval + { + echo -n "Obtaining an exclusive lock..." + if ! flock -x 9; then + echo ' failed.' + return 1 + fi + echo ' done' + + ${method} $@ + retval=$? + } 9> "$LOCAL_STATE_DIR/lock/subsys/lxc-sabayon-$lock_name" + + return $retval +} + +create_url () { + + local url="" + # Example of amd64 tarball url + # http://mirror.yandex.ru/sabayon/iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz + + if [ $arch == 'amd64' ] ; then + + if [ $release = 'DAILY' ] ; then + url="${MIRROR_URL}iso/daily/Sabayon_Linux_DAILY_amd64_tarball.tar.gz" + else + url="${MIRROR_URL}iso/monthly/Sabayon_Linux_${release}_amd64_tarball.tar.gz" + fi + else + # https://dockerbuilder.sabayon.org/Sabayon_Linux_16_armv7l.tar.bz2 + if [ $arch == 'armv7l' ] ; then + + # Currently $arch tarball is not on sabayon mirrored tree. + url="${MIRROR_URL}Sabayon_Linux_16_armv7l.tar.bz2" + + fi + fi + + echo $url +} + + +#=========================== Configure ===========================# + +unprivileged_rootfs() { + + pushd ${rootfs}/etc/systemd/system + + # Disable systemd-journald-audit.socket because it seems that doesn't + # start correctly on unprivileged container + ln -s /dev/null systemd-journald-audit.socket + + # Disable systemd-remount-fs.service because on unprivileged container + # systemd can't remount filesystem + ln -s /dev/null systemd-remount-fs.service + + # Remove mount of FUSE Control File system + ln -s /dev/null sys-fs-fuse-connections.mount + + # Change execution of service systemd-sysctl to avoid errors. + mkdir systemd-sysctl.service.d + cat < systemd-sysctl.service.d/00gentoo.conf +[Service] +ExecStart= +ExecStart=/usr/lib/systemd/systemd-sysctl --prefix=/etc/sysctl.d/ +EOF + + # Disable mount of hugepages + ln -s /dev/null dev-hugepages.mount + + popd + + pushd ${rootfs} + + # Disable sabayon-anti-fork-bomb limits (already apply to lxc container manager) + sed -i -e 's/^*/#*/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1 + sed -i -e 's/^root/#root/g' ./etc/security/limits.d/00-sabayon-anti-fork-bomb.conf || return 1 + + popd + + return 0 +} + +unprivileged_shift_owner () { + + # I use /usr/bin/fuidshift from LXD project. + + einfo "Executing: fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 ..." + + fuidshift ${rootfs} u:0:${mapped_uid}:65536 g:0:${mapped_gid}:65536 || + die 1 "Error on change owners of ${rootfs} directory" + + einfo "Done." + + # Fix permission of container directory + chmod a+rx ${path} + + return 0 +} + +systemd_container_tuning () { + + # To avoid error on start systemd-tmpfiles-setup service + # it is needed clean journal directory + rm -rf ${rootfs}/var/log/journal/ + + # Remove LVM service. Normally not needed on container system. + rm -rf ${rootfs}/etc/systemd/system/sysinit.target.wants/lvm2-lvmetad.service + + # Comment unneeded entry on /etc/fstab + sed -e 's/\/dev/#\/dev/g' -i ${rootfs}/etc/fstab + + # Fix this stupid error until fix is available on sabayon image + # /usr/lib/systemd/system-generators/gentoo-local-generator: line 4: cd: /etc/local.d: No such file or directory + mkdir ${rootfs}/etc/local.d/ + + mkdir ${rootfs}/etc/systemd/system/NetworkManager.service.d/ + cat < ${rootfs}/etc/systemd/system/NetworkManager.service.d/override.conf +[Service] +ExecStartPre=-/bin/ip -4 link set dev eth0 down +EOF + chmod 644 ${rootfs}/etc/systemd/system/NetworkManager.service.d/override.conf + + return 0 +} + +configure_container() { + local config="$1" + local hostname="$2" + local arch="$3" + local privileged_options="" + local unprivileged_options="" + + if [[ $unprivileged && $unprivileged == true ]] ; then + if [[ $flush_owner == true ]] ; then + unprivileged_options=" +lxc.idmap = u 0 ${mapped_uid} 65536 +lxc.idmap = g 0 ${mapped_gid} 65536 +" + fi + + unprivileged_options=" +$unprivileged_options + +# Force use of cgroup v1. Currently systemd doesn't support +# correctly cgroup v2. See: https://github.com/lxc/lxc/issues/1669 +# about discussion of default-hierarchy option. +lxc.init.cmd = /sbin/init systemd.legacy_systemd_cgroup_controller=yes + +# Include common configuration. +lxc.include = $LXC_TEMPLATE_CONFIG/sabayon.userns.conf +" + + else + privileged_options=" +## Allow any mknod (but not reading/writing the node) +lxc.cgroup.devices.allow = b *:* m +lxc.cgroup.devices.allow = c *:* m + +### /dev/pts/* +lxc.cgroup.devices.allow = c 136:* rwm +### /dev/tty +lxc.cgroup.devices.allow = c 5:0 rwm +### /dev/console +lxc.cgroup.devices.allow = c 5:1 rwm +### /dev/ptmx +lxc.cgroup.devices.allow = c 5:2 rwm +### fuse +lxc.cgroup.devices.allow = c 10:229 rwm + +" + fi + + cat <<-EOF >> "$config" +# Specify container architecture. +lxc.arch = $arch + +# Set hostname. +lxc.uts.name = $hostname + +# Include common configuration. +lxc.include = $LXC_TEMPLATE_CONFIG/sabayon.common.conf + +$unprivileged_options +$privileged_options +EOF +} + + +#============================= Main ==============================# + +parse_cmdline() { + + # Parse command options. + local short_options="a:dm:n:p:r:hu" + local long_options="arch:,debug,mirror:,name:,path:,release:,rootfs:,mapped-uid:,mapped-gid:,flush-owner,help" + + options=$(getopt -u -q -a -o "$short_options" -l "$long_options" -- "$@") + + eval set -- "$options" + + # Process command options. + while [ $# -gt 0 ]; do + case $1 in + -a | --arch) + arch=$2 + shift + ;; + -d | --debug) + debug='yes' + ;; + -m | --mirror) + mirror_url=$2 + shift + ;; + -n | --name) + name=$2 + shift + ;; + -p | --path) + path=$2 + shift + ;; + -r | --release) + release=$2 + shift + ;; + --rootfs) + rootfs=$2 + shift + ;; + -u | --unprivileged) + unprivileged=true + ;; + -h | --help) + usage + exit 1 + ;; + --mapped-uid) + mapped_uid=$2 + shift + ;; + --mapped-gid) + mapped_gid=$2 + shift + ;; + --flush-owner) + flush_owner=true + ;; + --) + break + ;; + *) + einfo "Unknown option: $1" + usage + exit 1 + ;; + esac + shift + done + + if [ "$(id -u)" != "0" ]; then + die 1 "This script must be run as 'root'" + fi + + # Validate options. + [ -n "$name" ] || die 1 'Missing required option --name' + [ -n "$path" ] || die 1 'Missing required option --path' + + if [ -z "$rootfs" ] && [ -f "$path/config" ]; then + rootfs="$(sed -nE 's/^lxc.rootfs\s*=\s*(.*)$/\1/p' "$path/config")" + fi + if [ -z "$rootfs" ]; then + rootfs="$path/rootfs" + fi + + [ -z "$path" ] && die 1 "'path' parameter is required." + + arch=$(parse_arch "$arch") \ + || die 1 "Unsupported architecture: $arch" + + [[ $unprivileged == true && $flush_owner == true &&-z "$mapped_uid" ]] && \ + die 1 'Missing required option --mapped-uid with --unprivileged option' + + [[ $unprivileged == true && $flush_owner == true && -z "$mapped_gid" ]] && \ + die 1 'Missing required option --mapped-gid with --unprivileged option' + + [[ $flush_owner == true && $unprivileged == false ]] && \ + die 1 'flush-owner require --unprivileged option' + + return 0 +} + +main () { + + local tarball="" + + # Set global variables. + RELEASE="${RELEASE:-"DAILY"}" + ARCH="${ARCH:-`uname -m`}" + OS="${OS:-"sabayon"}" + + einfo "Processing command line arguments: $@" + + # Parse command line options + parse_cmdline "$@" + + DEBUG="$debug" + MIRROR_URL="${mirror_url:-$(random_mirror_url)}" + + einfo "Use arch = $arch, mirror_url = $MIRROR_URL, path = $path, name = $name, release = $release, unprivileged = $unprivileged, rootfs = $rootfs, mapped_uid = $mapped_uid, mapped_gid = $mapped_gid, flush_owner = $flush_owner" + + [ "$debug" = 'yes' ] && set -x + + # Download sabayon tarball + tarball=$(create_url) + einfo "Fetching tarball $tarball..." + + # TODO: use only a compression mode + if [ $arch == 'amd64' ] ; then + fetch "${tarball}" | tar -xpz -C "${rootfs}" + else + if [ $arch == 'armv7l' ] ; then + fetch "${tarball}" | tar -xpj -C "${rootfs}" + fi + fi + + einfo "Tarball ${tarball} Extracted." + + systemd_container_tuning + + # Fix container for unprivileged mode. + if [[ $unprivileged == true ]] ; then + unprivileged_rootfs + if [[ $flush_owner == true ]] ; then + unprivileged_shift_owner + fi + fi + + return 0 +} + + +einfo "Prepare creation of sabayon container with params: $@ ($#)" + +# Here we go! +run_exclusively 'main' 10 main "$@" +configure_container "$path/config" "$name" "$arch" + +einfo "Container's rootfs and config have been created" +cat <<-EOF + Edit the config file $path/config to check/enable networking setup. + The installed system is preconfigured for a loopback and single network + interface configured via DHCP. + + To start the container, run "lxc-start -n $name". + The root password is not set; to enter the container run "lxc-attach -n $name". + + Note: From kenel >= 4.6 for use unprivileged containers it is needed this mount on host: + + mkdir /sys/fs/cgroup/systemd + mount -t cgroup -o none,name=systemd systemd /sys/fs/cgroup/systemd +EOF diff --git a/templates/lxc-slackware.in b/templates/lxc-slackware.in new file mode 100644 index 0000000..0c85c51 --- /dev/null +++ b/templates/lxc-slackware.in @@ -0,0 +1,793 @@ +#!/bin/bash + +# +# lxc: linux Container library + +# Authors: +# Daniel Lezcano + +# Template for slackware by Matteo Bernardini +# some parts are taken from the debian one (used as model) + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Add some directories to PATH in case we create containers with sudo +export PATH=/sbin:/usr/sbin:$PATH + +cache=${cache:-/var/cache/lxc/slackware} + +# Use the primary Slackware site by default, but please consider changing +# this to a closer mirror site. +MIRROR=${MIRROR:-http://ftp.slackware.com/pub/slackware} + +if [ -z "$arch" ]; then +case "$( uname -m )" in + i?86) arch=i486 ;; + arm*) arch=arm ;; + *) arch=$( uname -m ) ;; +esac +fi + +LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" + +configure_slackware() +{ +rootfs=$1 +hostname=$2 + +echo "Configuring..." ; echo + +# The next part contains excerpts taken from SeTconfig (written by +# Patrick Volkerding) from the slackware setup disk. +# But before pasting them just set a variable to use them as they are +T_PX=$rootfs + +( cd $T_PX ; chmod 755 ./ ) +( cd $T_PX ; chmod 755 ./var ) +if [ -d $T_PX/usr/src/linux ]; then + chmod 755 $T_PX/usr/src/linux +fi +if [ ! -d $T_PX/proc ]; then + mkdir $T_PX/proc + chown root.root $T_PX/proc +fi +if [ ! -d $T_PX/sys ]; then + mkdir $T_PX/sys + chown root.root $T_PX/sys +fi +chmod 1777 $T_PX/tmp +if [ ! -d $T_PX/var/spool/mail ]; then + mkdir -p $T_PX/var/spool/mail + chmod 755 $T_PX/var/spool + chown root.mail $T_PX/var/spool/mail + chmod 1777 $T_PX/var/spool/mail +fi + +echo "#!/bin/sh" > $T_PX/etc/rc.d/rc.keymap +echo "# Load the keyboard map. More maps are in /usr/share/kbd/keymaps." \ + >> $T_PX/etc/rc.d/rc.keymap +echo "if [ -x /usr/bin/loadkeys ]; then" >> $T_PX/etc/rc.d/rc.keymap +echo " /usr/bin/loadkeys us" >> $T_PX/etc/rc.d/rc.keymap +echo "fi" >> $T_PX/etc/rc.d/rc.keymap +chmod 755 $T_PX/etc/rc.d/rc.keymap + +# Network configuration is left to the user, that have to edit +# /etc/rc.d/rc.inet1.conf and /etc/resolv.conf of the container +# just set the hostname +cat < $rootfs/etc/HOSTNAME +$hostname.example.net +EOF +cp $rootfs/etc/HOSTNAME $rootfs/etc/hostname + +# make needed devices, from Chris Willing's MAKEDEV.sh +# http://www.vislab.uq.edu.au/howto/lxc/MAKEDEV.sh +DEV=$rootfs/dev +mkdir -p ${DEV} +mknod -m 666 ${DEV}/null c 1 3 +mknod -m 666 ${DEV}/zero c 1 5 +mknod -m 666 ${DEV}/random c 1 8 +mknod -m 666 ${DEV}/urandom c 1 9 +mkdir -m 755 ${DEV}/pts +mkdir -m 1777 ${DEV}/shm +mknod -m 666 ${DEV}/tty c 5 0 +mknod -m 600 ${DEV}/console c 5 1 +mknod -m 666 ${DEV}/tty0 c 4 0 +mknod -m 666 ${DEV}/tty1 c 4 1 +mknod -m 666 ${DEV}/tty2 c 4 2 +mknod -m 666 ${DEV}/tty3 c 4 3 +mknod -m 666 ${DEV}/tty4 c 4 4 +mknod -m 666 ${DEV}/tty5 c 4 5 +mknod -m 666 ${DEV}/full c 1 7 +mknod -m 600 ${DEV}/initctl p +mknod -m 660 ${DEV}/loop0 b 7 0 +mknod -m 660 ${DEV}/loop1 b 7 1 +ln -s pts/ptmx ${DEV}/ptmx +ln -s /proc/self/fd ${DEV}/fd + +echo "Adding an etc/fstab" +cat >$rootfs/etc/fstab <$rootfs/tmp/rcs.patch <<'EOF' +--- ./etc/rc.orig/rc.6 2012-08-15 01:03:12.000000000 +0200 ++++ ./etc/rc.d/rc.6 2013-02-17 10:26:30.888839354 +0100 +@@ -9,6 +9,12 @@ + # Author: Miquel van Smoorenburg + # Modified by: Patrick J. Volkerding, + # ++# minor tweaks for an lxc container ++# by Matteo Bernardini , ++# based also on Chris Willing's modifications ++# http://www.vislab.uq.edu.au/howto/lxc/rc.6 ++# a check for a container variable is made to jump sections ++container="lxc" + + # Set the path. + PATH=/sbin:/etc:/bin:/usr/bin +@@ -37,6 +43,9 @@ + ;; + esac + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Save the system time to the hardware clock using hwclock --systohc. + if [ -x /sbin/hwclock ]; then + # Check for a broken motherboard RTC clock (where ioports for rtc are +@@ -53,6 +62,8 @@ + fi + fi + ++fi # end container check ++ + # Run any local shutdown scripts: + if [ -x /etc/rc.d/rc.local_shutdown ]; then + /etc/rc.d/rc.local_shutdown stop +@@ -148,6 +159,9 @@ + sleep 2 + fi + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Shut down PCMCIA devices: + if [ -x /etc/rc.d/rc.pcmcia ]; then + . /etc/rc.d/rc.pcmcia stop +@@ -155,11 +169,16 @@ + /bin/sleep 5 + fi + ++fi # end container check ++ + # Turn off process accounting: + if [ -x /sbin/accton -a -r /var/log/pacct ]; then + /sbin/accton off + fi + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Terminate acpid before syslog: + if [ -x /etc/rc.d/rc.acpid -a -r /var/run/acpid.pid ]; then # quit + . /etc/rc.d/rc.acpid stop +@@ -170,6 +189,8 @@ + sh /etc/rc.d/rc.udev force-stop + fi + ++fi # end container check ++ + # Kill all remaining processes. + if [ ! "$1" = "fast" ]; then + echo "Sending all processes the SIGTERM signal." +@@ -179,6 +200,9 @@ + /sbin/killall5 -9 + fi + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Try to turn off quota. + if /bin/grep -q quota /etc/fstab ; then + if [ -x /sbin/quotaoff ]; then +@@ -187,6 +211,8 @@ + fi + fi + ++fi # end container check ++ + # Carry a random seed between reboots. + echo "Saving random seed from /dev/urandom in /etc/random-seed." + # Use the pool size from /proc, or 512 bytes: +@@ -205,6 +231,9 @@ + rm -f /var/lock/subsys/* + fi + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Turn off swap: + echo "Turning off swap." + /sbin/swapoff -a +@@ -216,6 +245,8 @@ + echo "Remounting root filesystem read-only." + /bin/mount -v -n -o remount,ro / + ++fi # end container check ++ + # This never hurts: + /bin/sync + +@@ -240,12 +271,17 @@ + done + fi + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Deactivate LVM volume groups: + if [ -r /etc/lvmtab -o -d /etc/lvm/backup ]; then + echo "Deactivating LVM volume groups:" + /sbin/vgchange -an --ignorelockingfailure + fi + ++fi # end container check ++ + # This never hurts again (especially since root-on-LVM always fails + # to deactivate the / logical volume... but at least it was + # remounted as read-only first) +@@ -258,6 +294,9 @@ + # This is to ensure all processes have completed on SMP machines: + wait + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + if [ -x /sbin/genpowerd ]; then + # See if this is a powerfail situation: + if /bin/egrep -q "FAIL|SCRAM" /etc/upsstatus 2> /dev/null ; then +@@ -274,6 +313,13 @@ + fi + fi + ++else ++ ++# confirm successful shutdown of the container ++echo ; echo "* container stopped. *" ; echo ++ ++fi # end container check ++ + # Now halt (poweroff with APM or ACPI enabled kernels) or reboot. + if [ "$command" = "reboot" ]; then + echo "Rebooting." +--- ./etc/rc.orig/rc.S 2012-09-13 21:38:34.000000000 +0200 ++++ ./etc/rc.d/rc.S 2013-02-17 09:39:41.579799641 +0100 +@@ -4,9 +4,18 @@ + # + # Mostly written by: Patrick J. Volkerding, + # ++# minor tweaks for an lxc container ++# by Matteo Bernardini , ++# based also on Chris Willing's modifications ++# http://www.vislab.uq.edu.au/howto/lxc/rc.S ++# a check for a container variable is made to jump sections ++container="lxc" + + PATH=/sbin:/usr/sbin:/bin:/usr/bin + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Try to mount /proc: + /sbin/mount -v proc /proc -n -t proc 2> /dev/null + +@@ -254,10 +263,27 @@ + read junk; + fi # Done checking root filesystem + ++else ++ # We really don't want to start udev in the container ++ if [ -f /etc/rc.d/rc.udev ]; then ++ chmod -x /etc/rc.d/rc.udev ++ fi ++ # Alsa won't work ++ if [ -f /etc/rc.d/rc.alsa ]; then ++ chmod -x /etc/rc.d/rc.alsa ++ fi ++ # This too ++ if [ -f /etc/rc.d/rc.loop ]; then ++ chmod -x /etc/rc.d/rc.loop ++ fi ++fi # end container check + + # Any /etc/mtab that exists here is old, so we start with a new one: + /bin/rm -f /etc/mtab{,~,.tmp} && /bin/touch /etc/mtab + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Add entry for / to /etc/mtab: + /sbin/mount -f -w / + +@@ -337,6 +363,8 @@ + # mounted read-write. + /sbin/swapon -a 2> /dev/null + ++fi # end container check ++ + # Clean up some temporary files: + rm -f /var/run/* /var/run/*/* /var/run/*/*/* /etc/nologin \ + /etc/dhcpc/*.pid /etc/forcefsck /etc/fastboot \ +@@ -364,7 +392,7 @@ + # if the first line of that file begins with the word 'Linux'. + # You are free to modify the rest of the file as you see fit. + if [ -x /bin/sed ]; then +- /bin/sed -i "{1s/^Linux.*/$(/bin/uname -sr)\./}" /etc/motd ++ /bin/sed -i "{1s/^Linux.*/$(/bin/uname -sr) lxc container\./}" /etc/motd + fi + + # If there are SystemV init scripts for this runlevel, run them. +@@ -372,6 +400,9 @@ + . /etc/rc.d/rc.sysvinit + fi + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Run serial port setup script: + # CAREFUL! This can make some systems hang if the rc.serial script isn't + # set up correctly. If this happens, you may have to edit the file from a +@@ -380,6 +411,8 @@ + sh /etc/rc.d/rc.serial start + fi + ++fi # end container check ++ + # Carry an entropy pool between reboots to improve randomness. + if [ -f /etc/random-seed ]; then + echo "Using /etc/random-seed to initialize /dev/urandom." +--- ./etc/rc.orig/rc.M 2012-09-25 19:47:07.000000000 +0200 ++++ ./etc/rc.d/rc.M 2013-02-17 09:39:41.579799641 +0100 +@@ -10,6 +10,10 @@ + # Author: Fred N. van Kempen, + # Heavily modified by Patrick Volkerding + # ++# minor tweaks for an lxc container ++# by Matteo Bernardini : ++# a check for a container variable is made to jump sections ++container="lxc" + + # Tell the viewers what's going to happen. + echo "Going multiuser..." +@@ -20,6 +24,9 @@ + /sbin/ldconfig & + fi + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Screen blanks after 15 minutes idle time, and powers down in one hour + # if the kernel supports APM or ACPI power management: + /bin/setterm -blank 15 -powersave powerdown -powerdown 60 +@@ -33,6 +40,8 @@ + /bin/hostname darkstar + fi + ++fi # end container check ++ + # Set the permissions on /var/log/dmesg according to whether the kernel + # permits non-root users to access kernel dmesg information: + if [ -r /proc/sys/kernel/dmesg_restrict ]; then +@@ -135,6 +144,9 @@ + chmod 755 / 2> /dev/null + chmod 1777 /tmp /var/tmp + ++# lxc container check ++if [ ! $container = "lxc" ]; then ++ + # Start APM or ACPI daemon. + # If APM is enabled in the kernel, start apmd: + if [ -e /proc/apm ]; then +@@ -146,6 +158,8 @@ + . /etc/rc.d/rc.acpid start + fi + ++fi # end container check ++ + # Update any existing icon cache files: + if find /usr/share/icons 2> /dev/null | grep -q icon-theme.cache ; then + for theme_dir in /usr/share/icons/* ; do +--- ./etc/rc.orig/rc.inet1 2012-08-05 19:13:27.000000000 +0200 ++++ ./etc/rc.d/rc.inet1 2013-02-17 09:39:41.579799641 +0100 +@@ -3,6 +3,11 @@ + # This script is used to bring up the various network interfaces. + # + # @(#)/etc/rc.d/rc.inet1 10.2 Sun Jul 24 12:45:56 PDT 2005 (pjv) ++# ++# minor tweaks for an lxc container ++# by Matteo Bernardini : ++# a check for a container variable is made to jump sections ++container="lxc" + + ############################ + # READ NETWORK CONFIG FILE # +@@ -105,6 +110,10 @@ + [ "${IFNAME[$i]}" = "${1}" ] && break + i=$(($i+1)) + done ++ ++ # lxc container check ++ if [ ! $container = "lxc" ]; then ++ + # If the interface is a bridge, then create it first: + [ -n "${BRNICS[$i]}" ] && br_open $i + # If the interface isn't in the kernel yet (but there's an alias for it in +@@ -115,6 +124,9 @@ + /sbin/modprobe ${1} + fi + fi ++ ++ fi # end container check ++ + if grep `echo ${1}: | cut -f 1 -d :`: /proc/net/dev 1> /dev/null ; then # interface exists + if ! /sbin/ifconfig | grep -w "${1}" 1>/dev/null || \ + ! /sbin/ifconfig ${1} | grep -w inet 1> /dev/null ; then # interface not up or not configured +EOF +( cd $rootfs ; patch -p1 < tmp/rcs.patch ; rm tmp/rcs.patch ) + +# restart rc.inet1 to have routing for the loop device +echo "/etc/rc.d/rc.inet1 restart" >> $rootfs/etc/rc.d/rc.local + +# reduce the number of local consoles: two should be enough +sed -i '/^c3\|^c4\|^c5\|^c6/s/^/# /' $rootfs/etc/inittab + +# better not use this in a container +sed -i 's/.*genpowerfail.*//' $rootfs/etc/inittab + +# add a message to rc.local that confirms successful container startup +echo "echo ; echo \"* container $name started. *\" ; echo" >> $rootfs/etc/rc.d/rc.local + +# borrow the time configuration from the local machine +cp -a /etc/localtime $rootfs/etc/localtime + +return 0 +} + +copy_slackware() +{ +rootfs=$1 + +# make a local copy of the installed filesystem +echo -n "Copying rootfs to $rootfs..." +mkdir -p $rootfs +cp -a $cache/rootfs-$release-$arch/* $rootfs/ || exit 1 + +# fix fstab with the actual path +sed -i "s|$cache/rootfs-$release-$arch|$rootfs|" $rootfs/etc/fstab + +return 0 +} + +install_slackware() +{ +rootfs=$1 +mkdir -p /var/lock/subsys/ +( +flock -n -x 9 +if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 +fi + +if [ "$arch" == "x86_64" ]; then + PKGMAIN=slackware64 +elif [ "$arch" == "arm" ]; then + PKGMAIN=slackwarearm +else + PKGMAIN=slackware +fi + +export CONF=$cache/slackpkg-conf +export ROOT=$cache/rootfs-$release-$arch + +mkdir -p $cache/cache-$release-$arch $cache/rootfs-$release-$arch \ + $cache/slackpkg-$release-$arch $CONF/templates + +echo "$MIRROR/$PKGMAIN-$release/" > $CONF/mirrors +touch $CONF/blacklist + +cat < $CONF/slackpkg.conf +# v2.8 +ARCH=$arch +TEMP=$cache/cache-$release-$arch +WORKDIR=$cache/slackpkg-$release-$arch +DELALL=off +CHECKMD5=on +CHECKGPG=on +CHECKSIZE=off +PRIORITY=( patches %PKGMAIN extra pasture testing ) +POSTINST=on +ONLY_NEW_DOTNEW=off +ONOFF=on +DOWNLOAD_ALL=on +DIALOG=off +BATCH=on +DEFAULT_ANSWER=y +USE_INCLUDES=on +SPINNING=off +EOF + +# thanks to Vincent Batts for this list of packages +# (that I modified a little :P) +# http://connie.slackware.com/~vbatts/minimal/ +cat < $CONF/templates/minimal-lxc.template +aaa_base +aaa_elflibs +aaa_terminfo +bash +bin +bzip2 +coreutils +dhcpcd +dialog +diffutils +e2fsprogs +elvis +etc +findutils +gawk +glibc-solibs +gnupg +grep +gzip +hostname +iputils +libunistring +logrotate +mpfr +net-tools +network-scripts +ncurses +openssh +openssl-solibs +pkgtools +procps-ng +sed +shadow +sharutils +slackpkg +sysklogd +sysvinit +sysvinit-functions +sysvinit-scripts +tar +udev +util-linux +wget +which +xz +EOF + +TEMPLATE=${TEMPLATE:-minimal-lxc} +if [ ! "$TEMPLATE" = "minimal-lxc" ]; then + if [ -f /etc/slackpkg/templates/$TEMPLATE.template ]; then + cat /etc/slackpkg/templates/$TEMPLATE.template \ + > $CONF/templates/$TEMPLATE.template + else + TEMPLATE="minimal-lxc" + fi +fi + +# clean previous installs +rm -fR $ROOT/* + +slackpkg -default_answer=n update +slackpkg install-template $TEMPLATE + +# add a slackpkg default mirror +echo "$MIRROR/$PKGMAIN-$release/" >> $ROOT/etc/slackpkg/mirrors + +# blacklist the devs package (we have to use our premade devices). +# do the same with the kernel packages (we use the host's one), +# but leave available headers and sources +echo "devs" >> $ROOT/etc/slackpkg/blacklist +sed -i \ + -e "s|^#kernel-|kernel-|" \ + -e "s|^kernel-headers|#kernel-headers|" \ + -e "s|^kernel-source|#kernel-source|" \ + $ROOT/etc/slackpkg/blacklist + +# force klog to use the system call interface to the kernel message +# buffers - needed for unprivileged containers +sed -i 's|3\ \-x|3 -x -s|' $ROOT/etc/rc.d/rc.syslog || true + +return 0 + +) 9>/var/lock/subsys/lxc + +return $? +} + +copy_configuration() +{ +path=$1 +rootfs=$2 +name=$3 + +cat <> $path/config + +lxc.uts.name = $name +lxc.arch = $arch + +lxc.mount.fstab = $rootfs/etc/fstab + +lxc.include = ${LXC_TEMPLATE_CONFIG}/slackware.common.conf +EOF + +if [ $? -ne 0 ]; then + echo "Failed to add configuration." + return 1 +fi + +return 0 +} + +clean() +{ +if [ ! -e $cache ]; then + exit 0 +fi + +# lock, so we won't purge while someone is creating a repository +( +flock -n -x 9 +if [ $? != 0 ]; then + echo "Cache repository is busy." + exit 1 +fi + +echo -n "Purging the download cache..." +rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 +exit 0 + +) 9>/var/lock/subsys/lxc +} + +usage() +{ +cat < --clean +EOF +return 0 +} + +options=$(getopt -o hp:n:a:r:c -l help,rootfs:,path:,name:,arch:,release:,clean -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +while true +do +case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -r|--release) release=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -c|--clean) clean=$2; shift 2;; + --) shift 1; break ;; + *) break ;; +esac +done + +if [ ! -z "$clean" -a -z "$path" ]; then + clean || exit 1 + exit 0 +fi + +type installpkg +if [ $? -ne 0 ]; then + echo "'installpkg' command is missing." + exit 1 +fi + +type slackpkg +if [ $? -ne 0 ]; then + echo "'slackpkg' command is missing." + exit 1 +fi + +if [ -z "$path" ]; then + echo "'path' parameter is required." + exit 1 +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'." + exit 1 +fi + +# If no release version was specified, use current +release=${release:-current} + +if [ -z "$name" ]; then + # no name given? set a default one + name=slackwarecontainer +fi + +# detect rootfs +config="$path/config" +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) + else + rootfs=$path/rootfs + fi +fi + +echo + +set -e + +install_slackware $rootfs +if [ $? -ne 0 ]; then + echo "Failed to install slackware." + exit 1 +fi + +echo + +configure_slackware $cache/rootfs-$release-$arch $name +if [ $? -ne 0 ]; then + echo "Failed to configure slackware for a container." + exit 1 +fi + +echo + +rootfs=$path/rootfs +copy_slackware $rootfs +if [ $? -ne 0 ]; then + echo "Failed to copy rootfs." + exit 1 +fi + +echo + +copy_configuration $path $rootfs $name +if [ $? -ne 0 ]; then + echo "Failed to write configuration file." + exit 1 +fi + +if [ ! -z $clean ]; then + clean || exit 1 + exit 0 +fi diff --git a/templates/lxc-sparclinux.in b/templates/lxc-sparclinux.in new file mode 100644 index 0000000..e5e686f --- /dev/null +++ b/templates/lxc-sparclinux.in @@ -0,0 +1,702 @@ +#!/bin/sh +# +# Template script for generating Linux for SPARC for LXC +# based on lxc-fedora, lxc-ubuntu +# +# Copyright © 2011 Wim Coekaerts +# Copyright © 2012 Dwight Engen +# Copyright � 2015 Wim Coekaerts +# +# Modified for Oracle Linux 5 +# Wim Coekaerts +# +# Modified for Oracle Linux 6,7 combined OL4,5,6 into one template script +# Dwight Engen +# +# Modified for Linux for SPARC 1.0 +# Wim Coekaerts +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +die() +{ + echo "failed: $1" + exit 1 +} + +is_btrfs_subvolume() +{ + if which btrfs >/dev/null 2>&1 && \ + btrfs subvolume list "$1" >/dev/null 2>&1; then + return 0 + fi + return 1 +} + +can_chcon() +{ + if which chcon >/dev/null 2>&1; then + selinuxenabled >/dev/null 2>&1 + return $? + fi + return 1 +} + +# fix up the container_rootfs +container_rootfs_patch() +{ + echo "Patching container rootfs $container_rootfs for Linux for SPARC $container_release_major.$container_release_minor" + + # copy ourself into the container to be used to --patch the rootfs when + # yum update on certain packages is done. we do this here instead of in + # container_rootfs_configure() in case the patching done in this function + # is updated in the future, we can inject the updated version of ourself + # into older containers. + if [ $container_rootfs != "/" ]; then + cp -f `readlink -f $0` $container_rootfs/usr/bin/lxc-patch + mkdir -p $container_rootfs/usr/share/yum-plugins + cp @DATADIR@/lxc/lxc-patch.py $container_rootfs/usr/share/yum-plugins + mkdir -p $container_rootfs/etc/yum/pluginconf.d + cat < $container_rootfs/etc/yum/pluginconf.d/lxc-patch.conf +[main] +enabled=1 +packages=dbus,initscripts,iptables,openssh-server,setup,selinux-policy,readahead,udev,util-linux,util-linux-ng +EOF + fi + + # "disable" selinux in the guest. The policy in the container isn't + # likely to match the hosts (unless host == guest exactly) and the + # kernel can only be enforcing one policy. + # + mkdir -p $container_rootfs/selinux + echo 0 > $container_rootfs/selinux/enforce + if [ -e $container_rootfs/etc/selinux/config ]; then + sed -i 's|SELINUX=enforcing|SELINUX=disabled|' $container_rootfs/etc/selinux/config + else + mkdir -p $container_rootfs/etc/selinux + echo "SELINUX=disabled" >$container_rootfs/etc/selinux/config + fi + sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/login + sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/login + sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*close|#session required pam_selinux.so close|' $container_rootfs/etc/pam.d/sshd + sed -i 's|session[ \t]*required[ \t]*pam_selinux.so[ \t]*open|#session required pam_selinux.so open|' $container_rootfs/etc/pam.d/sshd + + # setting /proc/$$/loginuid doesn't work under user namespace, which + # prevents logins from working + sed -i 's|session[ \t]*required[ \t]*pam_loginuid.so|#session required pam_loginuid.so|' $container_rootfs/etc/pam.d/sshd + sed -i 's|session[ \t]*required[ \t]*pam_loginuid.so|#session required pam_loginuid.so|' $container_rootfs/etc/pam.d/login + + if [ -f $container_rootfs/usr/sbin/selinuxenabled ]; then + mv $container_rootfs/usr/sbin/selinuxenabled $container_rootfs/usr/sbin/selinuxenabled.lxcorig + ln -s /bin/false $container_rootfs/usr/sbin/selinuxenabled + fi + + # ensure /dev/ptmx refers to the newinstance devpts of the container, or + # pty's will get crossed up with the hosts (https://lkml.org/lkml/2012/1/23/512) + rm -f $container_rootfs/dev/ptmx + ln -s pts/ptmx $container_rootfs/dev/ptmx + + # silence error in checking for selinux + sed -i 's|cat /proc/self/attr/current|cat /proc/self/attr/current 2>/dev/null|' $container_rootfs/etc/rc.sysinit + sed -i 's|cat /proc/self/attr/current|cat /proc/self/attr/current 2>/dev/null|' $container_rootfs/etc/rc.d/rc.sysinit + + # disable ipv6 + rm -f $container_rootfs/etc/sysconfig/network-scripts/init.ipv6-global + + # remove module stuff for iptables it just shows errors that are not + # relevant in a container + if [ -f "$container_rootfs/etc/sysconfig/iptables-config" ]; then + sed -i 's|IPTABLES_MODULES=".*|IPTABLES_MODULES=""|' $container_rootfs/etc/sysconfig/iptables-config + sed -i 's|IPTABLES_MODULES_UNLOAD=".*|IPTABLES_MODULES_UNLOAD="no"|' $container_rootfs/etc/sysconfig/iptables-config + fi + + # disable readahead in the container + if [ $container_release_major = "1" -a -e $container_rootfs/etc/sysconfig/readahead ]; then + rm -f $container_rootfs/etc/init/readahead-collector.conf + rm -f $container_rootfs/etc/init/readahead-disable-services.conf + sed -i 's|READAHEAD="yes"|READAHEAD="no"|' $container_rootfs/etc/sysconfig/readahead + fi + + + # no need to attempt to mount / + sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.sysinit + sed -i 's|mount -f /$|# LXC mount -f /|' $container_rootfs/etc/rc.d/rc.sysinit + sed -i 's|action \$"Remounting root filesystem|/bin/true # LXC action $"Remounting root filesystem|' $container_rootfs/etc/rc.sysinit + sed -i 's|action \$"Remounting root filesystem|/bin/true # LXC action $"Remounting root filesystem|' $container_rootfs/etc/rc.d/rc.sysinit + + # disable udev in the container + sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.sysinit + sed -i 's|.sbin.start_udev||' $container_rootfs/etc/rc.d/rc.sysinit + + sed -i 's|\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.d/init.d/halt + sed -i 's|^\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.sysinit + sed -i 's|^\[ -x /sbin/hwclock|\[ 0 -eq 1|' $container_rootfs/etc/rc.d/rc.sysinit + sed -i 's|^/sbin/hwclock|# LXC /sbin/nohwclock|' $container_rootfs/etc/rc.sysinit + sed -i 's|^/sbin/hwclock|# LXC /sbin/nohwclock|' $container_rootfs/etc/rc.d/rc.sysinit + + touch $container_rootfs/.nolvm + + # fix assumptions that plymouth is available + sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.sysinit + sed -i 's|\[ "$PROMPT" != no \] && plymouth|[ "$PROMPT" != no ] \&\& [ -n "$PLYMOUTH" ] \&\& plymouth|' $container_rootfs/etc/rc.d/rc.sysinit + rm -f $container_rootfs/etc/init/plymouth-shutdown.conf + rm -f $container_rootfs/etc/init/quit-plymouth.conf + rm -f $container_rootfs/etc/init/splash-manager.conf + + # dont try to unmount /dev/lxc devices + sed -i 's|&& $1 !~ /^\\/dev\\/ram/|\&\& $2 !~ /^\\/dev\\/lxc/ \&\& $1 !~ /^\\/dev\\/ram/|' $container_rootfs/etc/init.d/halt + + # don't try to unmount swap + sed -i 's|\[ -f /proc/swaps \]|# LXC [ -f /proc/swaps ]|' $container_rootfs/etc/init.d/halt + + sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mkdir -p /dev/shm \&\& mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.sysinit + sed -i 's|mount -n -o remount /dev/shm >/dev/null 2>&1$|mkdir -p /dev/shm \&\& mount -t tmpfs tmpfs /dev/shm # LXC|' $container_rootfs/etc/rc.d/rc.sysinit + + # there might be other services that are useless but the below set is a good start + # some of these might not exist in the image, so we silence chkconfig complaining + # about the service file not being found + for service in \ + acpid apmd auditd autofs cpuspeed dund gpm haldaemon hidd \ + ip6tables irqbalance iscsi iscsid isdn kdump kudzu \ + lm_sensors lvm2-monitor mdmonitor microcode_ctl \ + ntpd pcmcia postfix sendmail udev-post xfs ; + do + chroot $container_rootfs chkconfig 2>/dev/null $service off + done + + for service in rsyslog ; + do + chroot $container_rootfs chkconfig 2>/dev/null $service on + done +} + +container_rootfs_configure() +{ + container_rootfs_patch + echo "Configuring container for Linux for SPARC $container_release_major.$container_release_minor" + + # configure the network to use dhcp. we set DHCP_HOSTNAME so the guest + # will report its name and be resolv'able by the hosts dnsmasq + cat < $container_rootfs/etc/sysconfig/network-scripts/ifcfg-eth0 +DEVICE=eth0 +BOOTPROTO=dhcp +ONBOOT=yes +HOSTNAME=$name +DHCP_HOSTNAME=\`hostname\` +NM_CONTROLLED=no +TYPE=Ethernet +EOF + + cat < $container_rootfs/etc/sysconfig/network +NETWORKING=yes +NETWORKING_IPV6=no +HOSTNAME=$name +EOF + + # set minimal hosts + echo "127.0.0.1 localhost $name" > $container_rootfs/etc/hosts + + # this file has to exist for libvirt/Virtual machine monitor to boot the container + touch $container_rootfs/etc/mtab + + # setup console and tty[1-4] for login. note that /dev/console and + # /dev/tty[1-4] will be symlinks to the ptys /dev/lxc/console and + # /dev/lxc/tty[1-4] so that package updates can overwrite the symlinks. + # lxc will maintain these links and bind mount ptys over /dev/lxc/* + # since lxc.tty.dir is specified in the config. + + # allow root login on console, tty[1-4], and pts/0 for libvirt + echo "# LXC (Linux Containers)" >>$container_rootfs/etc/securetty + echo "lxc/console" >>$container_rootfs/etc/securetty + for i in 1 2 3 4; do + echo "lxc/tty$i" >>$container_rootfs/etc/securetty + done + echo "# For libvirt/Virtual Machine Monitor" >>$container_rootfs/etc/securetty + for i in 0 1 2 3 4; do + echo "pts/$i" >>$container_rootfs/etc/securetty + done + + # prevent mingetty from calling vhangup(2) since it fails with userns + if [ -f $container_rootfs/etc/init/tty.conf ]; then + sed -i 's|mingetty|mingetty --nohangup|' $container_rootfs/etc/init/tty.conf + fi + + # create maygetty which only spawns a getty on the console when running + # under lxc, not libvirt-lxc which symlinks /dev/console to the same pty + # as /dev/tty1 + cat <$container_rootfs/sbin/maygetty +#!/bin/sh +if [ "\$container" = "lxc" ]; then + exec /sbin/mingetty \$@ +fi +exec sleep infinity +EOF + chmod 755 $container_rootfs/sbin/maygetty + + cat < $container_rootfs/etc/init/console.conf +# console - getty +# +# This service maintains a getty on the console from the point the system is +# started until it is shut down again. + +start on stopped rc RUNLEVEL=[2345] +stop on runlevel [!2345] +env container + +respawn +exec /sbin/maygetty --nohangup --noclear /dev/console +EOF + + cat < $container_rootfs/etc/init/power-status-changed.conf +# power-status-changed - used to cleanly shut down the container +# +# This task is run whenever init receives SIGPWR +# Used to shut down the machine. + +start on power-status-changed + +exec init 0 +EOF + + # start with a clean /var/log/messages + rm -f $container_rootfs/var/log/messages + + # set initial timezone as on host + if [ -f /etc/sysconfig/clock ]; then + . /etc/sysconfig/clock + echo ZONE=$ZONE > $container_rootfs/etc/sysconfig/clock + chroot $container_rootfs tzdata-update + else + echo "Timezone in container is not configured. Adjust it manually." + fi + + # add oracle user + chroot $container_rootfs useradd -m -s /bin/bash oracle + printf "Added container user:\033[1moracle\033[0m\n" + printf "Added container user:\033[1mroot\033[0m\n" +} + +# create the container's lxc config file +container_config_create() +{ + echo "Create configuration file $cfg_dir/config" + mkdir -p $cfg_dir || die "unable to create config dir $cfg_dir" + + echo "# Common configuration" >> $cfg_dir/config + if [ -e "@LXCTEMPLATECONFIG@/sparclinux.common.conf" ]; then + echo "lxc.include = @LXCTEMPLATECONFIG@/sparclinux.common.conf" >> $cfg_dir/config + fi + + cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" +# Container configuration for Linux for SPARC $container_release_major.$container_release_minor +lxc.arch = $arch +lxc.uts.name = $name +EOF + grep -q "^lxc.rootfs.path" $cfg_dir/config 2>/dev/null || echo "lxc.rootfs.path = $container_rootfs" >> $cfg_dir/config + + echo "lxc.cap.drop = sys_resource" >>$cfg_dir/config + + echo "lxc.cap.drop = setfcap setpcap" >>$cfg_dir/config + + echo "# Networking" >>$cfg_dir/config + # see if the default network settings were already specified + lxc_network_type=`grep '^lxc.net.0.type' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + if [ -z "$lxc_network_type" ]; then + echo "lxc.net.0.type = veth" >>$cfg_dir/config + lxc_network_type=veth + fi + + lxc_network_link=`grep '^lxc.net.0.link' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + if [ -z "$lxc_network_link" ]; then + echo "lxc.net.0.link = lxcbr0" >>$cfg_dir/config + lxc_network_link=lxcbr0 + fi + + lxc_network_hwaddr=`grep '^lxc.net.0.hwaddr' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + if [ -z "$lxc_network_hwaddr" ]; then + # generate a hwaddr for the container + # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303 + local hwaddr="00:16:3e:`dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \ + head -n1 | awk '{print $2}' | cut -c1-6 | \ + sed 's/\(..\)/\1:/g; s/.$//'`" + echo "lxc.net.0.hwaddr = $hwaddr" >>$cfg_dir/config + fi + + lxc_network_flags=`grep '^lxc.net.0.flags' $cfg_dir/config | awk -F'[= \t]+' '{ print $2 }'` + if [ -z "$lxc_network_flags" ]; then + echo "lxc.net.0.flags = up" >>$cfg_dir/config + fi + + cat <> $cfg_dir/config || die "unable to create $cfg_dir/config" +lxc.net.0.name = eth0 +lxc.net.0.mtu = 1500 +EOF +} + +container_rootfs_clone() +{ + if is_btrfs_subvolume $template_rootfs; then + # lxc-create already made $container_rootfs a btrfs subvolume, but + # in this case we want to snapshot the original subvolume so we we + # have to delete the one that lxc-create made + btrfs subvolume delete $container_rootfs + btrfs subvolume snapshot $template_rootfs $container_rootfs || die "btrfs clone template" + else + echo "Copying rootfs ..." + cp -axT $template_rootfs $container_rootfs || die "copy template" + fi +} + +container_rootfs_repo_create() +{ + echo "# LXC generated .repo file" >$1 + echo "[$2]" >>$1 + echo "name=Linux for SPARC $container_release_major.$container_release_minor ($basearch)" >>$1 + echo "baseurl=$3/" >>$1 + echo "enabled=1" >>$1 + echo "skip_if_unavailable=1" >>$1 + + if [ "$4" != "" ]; then + echo "gpgkey=$yum_url/RPM-GPG-KEY-oracle-ol$container_release_major" >>$1 + echo "gpgcheck=1" >>$1 + else + echo "gpgcheck=0" >>$1 + fi +} + +container_rootfs_dev_create() +{ + # create required devices. note that /dev/console will be created by lxc + # or libvirt itself to be a symlink to the right pty. + # take care to not nuke /dev in case $container_rootfs isn't set + dev_path="$container_rootfs/dev" + if [ $container_rootfs != "/" -a -d $dev_path ]; then + rm -rf $dev_path + fi + mkdir -p $dev_path + if can_chcon; then + # ensure symlinks created in /dev have the right context + chcon -t device_t $dev_path + fi + mknod -m 666 $dev_path/null c 1 3 + mknod -m 666 $dev_path/zero c 1 5 + mknod -m 666 $dev_path/random c 1 8 + mknod -m 666 $dev_path/urandom c 1 9 + mkdir -m 755 $dev_path/pts + mkdir -m 1777 $dev_path/shm + mknod -m 666 $dev_path/tty c 5 0 + mknod -m 666 $dev_path/tty1 c 4 1 + mknod -m 666 $dev_path/tty2 c 4 2 + mknod -m 666 $dev_path/tty3 c 4 3 + mknod -m 666 $dev_path/tty4 c 4 4 + mknod -m 666 $dev_path/full c 1 7 + mknod -m 600 $dev_path/initctl p + + # set selinux labels same as host + if can_chcon; then + for node in null zero random urandom pts shm \ + tty tty0 tty1 tty2 tty3 tty4 full ; + do + chcon --reference /dev/$node $dev_path/$node 2>/dev/null + done + fi +} + +container_rootfs_create() +{ + if can_chcon; then + chcon --reference / $container_rootfs 2>/dev/null + fi + + cmds="rpm wget yum" + for cmd in $cmds; do + which $cmd >/dev/null 2>&1 + if [ $? -ne 0 ]; then + die "The $cmd command is required, please install it" + fi + done + + mkdir -p @LOCALSTATEDIR@/lock/subsys + ( + flock -x 9 + if [ $? -ne 0 ]; then + die "The template is busy." + fi + + echo "Yum installing release $container_release_major.$container_release_minor for $basearch" + + if [ -n "$repourl" ]; then + yum_url=$repourl + else + yum_url=http://yum.oracle.com + fi + + if [ -n "$baseurl" ]; then + # create .repo pointing at baseurl + repo="lxc-install" + mkdir -p $container_rootfs/etc/yum.repos.d + container_rootfs_repo_create \ + $container_rootfs/etc/yum.repos.d/lxc-install.repo $repo $baseurl + else + # get public-yum repo file + if [ $container_release_major = "1" ]; then + repofile=yum-linux-sparc64.repo + else + die "Unsupported release $container_release_major" + fi + + mkdir -p $container_rootfs/etc/yum.repos.d + wget -q $yum_url/$repofile -O $container_rootfs/etc/yum.repos.d/$repofile + if [ $? -ne 0 ]; then + die "Unable to download repo file $yum_url/$repofile, release unavailable" + fi + + # yum will take $basearch from host, so force the arch we want + sed -i "s|\$basearch|$basearch|" $container_rootfs/etc/yum.repos.d/$repofile + + # replace url if they specified one + if [ -n "$repourl" ]; then + sed -i "s|baseurl=http://yum.oracle.com/|baseurl=$repourl/repo|" $container_rootfs/etc/yum.repos.d/$repofile + sed -i "s|gpgkey=http://yum.oracle.com|gpgkey=$repourl|" $container_rootfs/etc/yum.repos.d/$repofile + + fi + + # disable all repos, then enable the repo for the version we are installing. + if [ $container_release_minor = "latest" ]; then + repo="lfs"_$container_release_minor + else + die "Unsupported release $container_release_major.$container_release_minor" + fi + sed -i "s|enabled=1|enabled=0|" $container_rootfs/etc/yum.repos.d/$repofile + sed -i "/\[$repo\]/,/\[/ s/enabled=0/enabled=1/" $container_rootfs/etc/yum.repos.d/$repofile + fi + + container_rootfs_dev_create + + # don't put devpts,proc, nor sysfs in here, it will already be mounted for us by lxc/libvirt + echo "" >$container_rootfs/etc/fstab + + # create rpm db, download and yum install minimal packages + mkdir -p $container_rootfs/var/lib/rpm + rpm --root $container_rootfs --initdb + yum_args="--installroot $container_rootfs --disablerepo=* --enablerepo=$repo -y --nogpgcheck" + min_pkgs="yum initscripts passwd rsyslog vim-minimal openssh-server openssh-clients dhclient chkconfig rootfiles policycoreutils sparclinux-release" + + # we unshare the mount namespace because yum installing the ol4 + # packages causes $rootfs/proc to be mounted on + lxc-unshare -s MOUNT yum -- $yum_args install $min_pkgs $user_pkgs + if [ $? -ne 0 ]; then + die "Failed to download and install the rootfs, aborting." + fi + + # rsyslog and pam depend on coreutils for some common commands in + # their POSTIN scriptlets, but coreutils wasn't installed yet. now + # that coreutils is installed, reinstall the packages so their POSTIN + # runs right. similarly, libutempter depends on libselinux.so.1 when + # it runs /usr/sbin/groupadd, so reinstall it too + redo_pkgs="" + if [ x"$redo_pkgs" != x ]; then + rpm --root $container_rootfs --nodeps -e $redo_pkgs + lxc-unshare -s MOUNT yum -- $yum_args install $redo_pkgs + if [ $? -ne 0 ]; then + die "Unable to reinstall packages" + fi + fi + + # these distributions put the rpm database in a place the guest is + # not expecting it, so move it + if [ $host_distribution = "Ubuntu" -o $host_distribution = "Debian" ]; then + mv $container_rootfs/$HOME/.rpmdb/* $container_rootfs/var/lib/rpm + fi + + # if the native rpm created the db with Hash version 9, we need to + # downgrade it to Hash version 8 for use with OL5.x + db_version=`file $container_rootfs/var/lib/rpm/Packages | \ + grep -o 'version [0-9]*' |awk '{print $2}'` + + # the host rpm may not be the same as the guest, rebuild the db with + # the guest rpm version + echo "Rebuilding rpm database" + rm -f $container_rootfs/var/lib/rpm/__db* + chroot $container_rootfs rpm --rebuilddb >/dev/null 2>&1 + + ) 9>@LOCALSTATEDIR@/lock/subsys/lxc-sparclinux-$name + if [ $? -ne 0 ]; then + exit 1 + fi +} + +container_release_get() +{ + if [ -f $1/etc/sparclinux-release ]; then + container_release_version=`cat $1/etc/sparclinux-release |awk '/^Linux/ {print $5}'` + container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` + container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` + else + echo "Unable to determine container release version" + exit 1 + fi +} + +usage() +{ + cat < architecture (sparc64) + -R|--release= release to download for the new container + --rootfs= rootfs path + -r|--rpms= additional rpms to install into container + -u|--url= replace yum repo url (ie. Oracle public-yum mirror) + --baseurl= use package repository (ie. file:///mnt) + arch and release must also be specified + -t|--templatefs= copy/clone rootfs at path instead of downloading + -P|--patch= only patch the rootfs at path for use as a container + -h|--help + +Release is of the format "major.minor", for example "1.0" or "1.latest" +This template supports Linux for SPARC release 1.0 +EOF + return 0 +} + +options=$(getopt -o hp:n:a:R:r:u:t: -l help,rootfs:,path:,name:,arch:,release:,rpms:,url:,templatefs:,patch:,baseurl: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi + +eval set -- "$options" +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) cfg_dir=$2; shift 2;; + --rootfs) container_rootfs=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -R|--release) container_release_version=$2; shift 2;; + -r|--rpms) user_pkgs=$2; shift 2;; + -u|--url) repourl=$2; shift 2;; + -t|--templatefs) template_rootfs=$2; shift 2;; + --patch) patch_rootfs=$2; shift 2;; + --baseurl) baseurl=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +# make sure mandatory args are given and valid +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ -n "$baseurl" ]; then + if [ "$arch" = "" -o "$container_release_version" = "" ]; then + echo "The --arch and --release must be specified when using --baseurl" + usage + exit 1 + fi +fi + +if [ "$arch" = "" ]; then + arch=$(uname -m) +fi + +if [ -n "$patch_rootfs" ]; then + container_rootfs="$patch_rootfs" + container_release_get $container_rootfs + container_rootfs_patch + exit 0 +fi + +if [ -z $name ]; then + echo "Container name must be given" + usage + exit 1 +fi + +if [ -z $cfg_dir ]; then + echo "Configuration directory must be given, check lxc-create" + usage + exit 1 +fi + +basearch=$arch + +if [ "$arch" != "sparc64" ]; then + echo "Bad architecture given, check lxc-create" + usage + exit 1 +fi + +if [ -f /etc/sparclinux-release ]; then + host_distribution="SPARCLinux" + host_release_version=`cat /etc/sparclinux-release |awk '{print $5}'` + host_release_major=`echo $host_release_version |awk -F '.' '{print $1}'` + host_release_minor=`echo $host_release_version |awk -F '.' '{print $2}'` +else + echo "Unable to determine host distribution" + exit 1 +fi + +echo "Host is $host_distribution $host_release_version" + +if [ -z "$container_rootfs" ]; then + container_rootfs="$cfg_dir/rootfs" +fi + +if [ -n "$template_rootfs" ]; then + container_release_get $template_rootfs +else + if [ -z "$container_release_version" ]; then + if [ $host_distribution = "SPARCLinux" ]; then + container_release_version=$host_release_version + else + echo "No release specified with -R, defaulting to 1.latest" + container_release_version="1.latest" + fi + fi + container_release_major=`echo $container_release_version |awk -F '.' '{print $1}'` + container_release_minor=`echo $container_release_version |awk -F '.' '{print $2}'` +fi + +container_config_create +if [ -n "$template_rootfs" ]; then + container_rootfs_clone +else + container_rootfs_create +fi + +container_release_get $container_rootfs + +container_rootfs_configure + +echo "Container : $container_rootfs" +echo "Config : $cfg_dir/config" +echo "Network : eth0 ($lxc_network_type) on $lxc_network_link" diff --git a/templates/lxc-sshd.in b/templates/lxc-sshd.in new file mode 100644 index 0000000..e6f90ad --- /dev/null +++ b/templates/lxc-sshd.in @@ -0,0 +1,276 @@ +#!/bin/bash + +# +# lxc: linux Container library + +# Authors: +# Daniel Lezcano + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +install_sshd() +{ + rootfs=$1 + + tree="\ +$rootfs/var/empty/sshd \ +$rootfs/var/lib/empty/sshd \ +$rootfs/etc/init.d \ +$rootfs/etc/rc.d \ +$rootfs/etc/ssh \ +$rootfs/etc/sysconfig/network-scripts \ +$rootfs/dev/shm \ +$rootfs/run/sshd \ +$rootfs/proc \ +$rootfs/sys \ +$rootfs/bin \ +$rootfs/sbin \ +$rootfs/usr \ +$rootfs/tmp \ +$rootfs/home \ +$rootfs/root \ +$rootfs/lib \ +$rootfs/lib64" + + mkdir -p $tree + if [ $? -ne 0 ]; then + return 1 + fi + + ln -s /run $rootfs/var/run + if [ $? -ne 0 ]; then + return 1 + fi + + return 0 +} + +configure_sshd() +{ + rootfs=$1 + + cat < $rootfs/etc/passwd +root:x:0:0:root:/root:/bin/bash +sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin +EOF + + cat < $rootfs/etc/group +root:x:0:root +sshd:x:74: +EOF + +ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key +ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key + + # by default setup root password with no password + cat < $rootfs/etc/ssh/sshd_config +Port 22 +Protocol 2 +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_dsa_key +UsePrivilegeSeparation yes +SyslogFacility AUTH +LogLevel INFO +LoginGraceTime 120 +PermitRootLogin yes +StrictModes yes +PubkeyAuthentication yes +IgnoreRhosts yes +HostbasedAuthentication no +PermitEmptyPasswords yes +ChallengeResponseAuthentication no +EOF + + if [ -n "$auth_key" -a -f "$auth_key" ]; then + u_path="/root/.ssh" + root_u_path="$rootfs/$u_path" + mkdir -p $root_u_path + cp $auth_key "$root_u_path/authorized_keys" + chown -R 0:0 "$rootfs/$u_path" + chmod 700 "$rootfs/$u_path" + echo "Inserted SSH public key from $auth_key into $rootfs/$u_path" + fi + + return 0 +} + +copy_configuration() +{ + path=$1 + rootfs=$2 + name=$3 + + init_path=$(realpath --relative-to=/ $(readlink -f /sbin/init)) + + grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config +cat <> $path/config +lxc.uts.name = $name +lxc.pty.max = 1024 +lxc.cap.drop = sys_module mac_admin mac_override sys_time + +# When using LXC with apparmor, uncomment the next line to run unconfined: +#lxc.apparmor.profile = unconfined + +lxc.mount.entry = /dev dev none ro,bind 0 0 +lxc.mount.entry = /lib lib none ro,bind 0 0 +lxc.mount.entry = /bin bin none ro,bind 0 0 +lxc.mount.entry = /usr usr none ro,bind 0 0 +lxc.mount.entry = /sbin sbin none ro,bind 0 0 +lxc.mount.entry = tmpfs run/sshd tmpfs mode=0644 0 0 +lxc.mount.entry = @LXCTEMPLATEDIR@/lxc-sshd $init_path none ro,bind 0 0 +lxc.mount.entry = /etc/init.d etc/init.d none ro,bind 0 0 + +lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed +EOF + + # Oracle Linux and Fedora need the following two bind mounted + if [ -d /etc/sysconfig/network-scripts ]; then + cat <> $path/config +lxc.mount.entry = /etc/sysconfig/network-scripts etc/sysconfig/network-scripts none ro,bind 0 0 +EOF + fi + + if [ -d /etc/rc.d ]; then + cat <> $path/config +lxc.mount.entry = /etc/rc.d etc/rc.d none ro,bind 0 0 +EOF + fi + + # if no .ipv4 section in config, then have the container run dhcp + grep -q "^lxc.net.0.ipv4.address" $path/config || touch $rootfs/run-dhcp + + if [ "$(uname -m)" = "x86_64" ]; then + cat <> $path/config +lxc.mount.entry = /lib64 lib64 none ro,bind 0 0 +EOF + fi +} + +usage() +{ + cat < [--rootfs=] +EOF + return 0 +} + +check_for_cmd() +{ + cmd_path=`type $1` + if [ $? -ne 0 ]; then + echo "The command '$1' $cmd_path is not accessible on the system" + exit 1 + fi + # we use cut instead of awk because awk is alternatives symlink on ubuntu + # and /etc/alternatives isn't bind mounted + cmd_path=`echo $cmd_path |cut -d ' ' -f 3` +} + +options=$(getopt -o hp:n:S: -l help,rootfs:,path:,name:,auth-key: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + -p|--path) path=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -S|--auth-key) auth_key=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +if [ $0 = "/sbin/init" ]; then + + PATH="$PATH:/bin:/sbin:/usr/sbin" + check_for_cmd @SBINDIR@/init.lxc + check_for_cmd sshd + sshd_path=$cmd_path + + # run dhcp? + if [ -f /run-dhcp ]; then + check_for_cmd dhclient + check_for_cmd ifconfig + touch /etc/fstab + rm -f /dhclient.conf + cat > /dhclient.conf << EOF +send host-name = gethostname(); +EOF + ifconfig eth0 up + dhclient eth0 -cf /dhclient.conf + echo "Container IP address:" + ifconfig eth0 |grep inet + fi + + exec @SBINDIR@/init.lxc -- $sshd_path + exit 1 +fi + +if [ -z "$path" ]; then + echo "'path' parameter is required" + exit 1 +fi + +# detect rootfs +config="$path/config" +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) + else + rootfs=$path/rootfs + fi +fi + +install_sshd $rootfs +if [ $? -ne 0 ]; then + echo "failed to install sshd's rootfs" + exit 1 +fi + +configure_sshd $rootfs +if [ $? -ne 0 ]; then + echo "failed to configure sshd template" + exit 1 +fi + +copy_configuration $path $rootfs $name +if [ $? -ne 0 ]; then + echo "failed to write configuration file" + exit 1 +fi diff --git a/templates/lxc-ubuntu-cloud.in b/templates/lxc-ubuntu-cloud.in new file mode 100644 index 0000000..2bf534a --- /dev/null +++ b/templates/lxc-ubuntu-cloud.in @@ -0,0 +1,374 @@ +#!/bin/bash + +# template script for generating ubuntu container for LXC based on released +# cloud images. +# +# Copyright © 2012 Serge Hallyn +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +set -e + +STATE_DIR="@LOCALSTATEDIR@" +HOOK_DIR="@LXCHOOKDIR@" +CLONE_HOOK_FN="$HOOK_DIR/ubuntu-cloud-prep" +LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" +KNOWN_RELEASES="precise trusty xenial yakkety zesty" +skip_arch_check=${UCTEMPLATE_SKIP_ARCH_CHECK:-0} + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +if [ -r /etc/default/lxc ]; then + . /etc/default/lxc +fi + +am_in_userns() { + [ -e /proc/self/uid_map ] || { echo no; return; } + [ "$(wc -l /proc/self/uid_map | awk '{ print $1 }')" -eq 1 ] || { echo yes; return; } + line=$(awk '{ print $1 " " $2 " " $3 }' /proc/self/uid_map) + [ "$line" = "0 0 4294967295" ] && { echo no; return; } + echo yes +} + +in_userns=0 +[ $(am_in_userns) = "yes" ] && in_userns=1 + +copy_configuration() +{ + path=$1 + rootfs=$2 + name=$3 + arch=$4 + release=$5 + + if [ $arch = "i386" ]; then + arch="i686" + fi + + # if there is exactly one veth network entry, make sure it has an + # associated hwaddr. + nics=`grep -e '^lxc\.net\.0\.type[ \t]*=[ \t]*veth' $path/config | wc -l` + if [ $nics -eq 1 ]; then + grep -q "^lxc.net.0.hwaddr" $path/config || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config + fi + + # Generate the configuration file + ## Relocate all the network config entries + sed -i -e "/lxc.net.0/{w ${path}/config-network" -e "d}" $path/config + + ## Relocate any other config entries + sed -i -e "/lxc./{w ${path}/config-auto" -e "d}" $path/config + + ## Add all the includes + echo "" >> $path/config + echo "# Common configuration" >> $path/config + if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.common.conf" ]; then + echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.common.conf" >> $path/config + fi + if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.${release}.conf" ]; then + echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.${release}.conf" >> $path/config + fi + if [ $in_userns -eq 1 ] && [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.userns.conf" ]; then + echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu-cloud.userns.conf" >> $path/config + fi + + ## Add the container-specific config + echo "" >> $path/config + echo "# Container specific configuration" >> $path/config + [ -e "$path/config-auto" ] && cat $path/config-auto >> $path/config && rm $path/config-auto + grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config + cat <> $path/config +lxc.uts.name = $name +lxc.arch = $arch +EOF + + ## Re-add the previously removed network config + echo "" >> $path/config + echo "# Network configuration" >> $path/config + cat $path/config-network >> $path/config + rm $path/config-network + + # Set initial timezone as on host + if [ -f /etc/timezone ]; then + cat /etc/timezone > $rootfs/etc/timezone + chroot $rootfs dpkg-reconfigure -f noninteractive tzdata + elif [ -f /etc/sysconfig/clock ]; then + . /etc/sysconfig/clock + echo $ZONE > $rootfs/etc/timezone + chroot $rootfs dpkg-reconfigure -f noninteractive tzdata + else + echo "Timezone in container is not configured. Adjust it manually." + fi + + # rmdir /dev/shm for containers that have /run/shm + # I'm afraid of doing rm -rf $rootfs/dev/shm, in case it did + # get bind mounted to the host's /run/shm. So try to rmdir + # it, and in case that fails move it out of the way. + # NOTE: This can only be removed once 12.04 goes out of support + if [ ! -L $rootfs/dev/shm ] && [ -e $rootfs/dev/shm ]; then + rmdir $rootfs/dev/shm 2>/dev/null || mv $rootfs/dev/shm $rootfs/dev/shm.bak + ln -s /run/shm $rootfs/dev/shm + fi + + return 0 +} + +usage() +{ + cat < ]: Release name of container, defaults to host +[ --rootfs ]: Path in which rootfs will be placed +[ -a | --arch ]: Architecture of container, defaults to host architecture +[ -T | --tarball ]: Location of tarball +[ -d | --debug ]: Run with 'set -x' to debug errors +[ -s | --stream]: Use specified stream rather than 'tryreleased' + +Additionally, clone hooks can be passed through (ie, --userdata). For those, +see: + $CLONE_HOOK_FN --help +EOF + return 0 +} + +options=$(getopt -o a:hp:r:n:Fi:CLS:T:ds:u: -l arch:,help,rootfs:,path:,release:,name:,flush-cache,hostid:,auth-key:,cloud,no_locales,tarball:,debug,stream:,userdata:,vendordata:,mapped-uid:,mapped-gid: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +mapped_uid=-1 +mapped_gid=-1 +# default release is trusty, or the systems release if recognized +release=trusty +if [ -f /etc/lsb-release ]; then + . /etc/lsb-release + rels=$(ubuntu-distro-info --supported 2>/dev/null) || + rels="$KNOWN_RELEASES" + for r in $rels; do + [ "$DISTRIB_CODENAME" = "$r" ] && release="$r" + done +fi + +# Code taken from debootstrap +if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then + arch=`/usr/bin/dpkg --print-architecture` +elif type udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then + arch=`/usr/bin/udpkg --print-architecture` +else + arch=$(uname -m) + if [ "$arch" = "i686" ]; then + arch="i386" + elif [ "$arch" = "x86_64" ]; then + arch="amd64" + elif [ "$arch" = "armv7l" ]; then + # note: arm images don't exist before oneiric; are called armhf in + # trusty and later; and are not supported by the query, so we don't actually + # support them yet (see check later on). When Query2 is available, + # we'll use that to enable arm images. + arch="armhf" + elif [ "$arch" = "aarch64" ]; then + arch="arm64" + elif [ "$arch" = "ppc64le" ]; then + arch="ppc64el" + fi +fi + +debug=0 +hostarch=$arch +cloud=0 +locales=1 +flushcache=0 +stream="tryreleased" +cloneargs=() +while true +do + case "$1" in + -h|--help) usage $0 && exit 1;; + -p|--path) path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -F|--flush-cache) flushcache=1; shift 1;; + -r|--release) release=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -T|--tarball) tarball=$2; shift 2;; + -d|--debug) debug=1; shift 1;; + -s|--stream) stream=$2; shift 2;; + --rootfs) rootfs=$2; shift 2;; + -L|--no?locales) cloneargs[${#cloneargs[@]}]="--no-locales"; shift 1;; + -i|--hostid) cloneargs[${#cloneargs[@]}]="--hostid=$2"; shift 2;; + -u|--userdata) cloneargs[${#cloneargs[@]}]="--userdata=$2"; shift 2;; + -V|--vendordata) cloneargs[${#cloneargs[@]}]="--vendordata=$2"; shift 2;; + -C|--cloud) cloneargs[${#cloneargs[@]}]="--cloud"; shift 1;; + -S|--auth-key) cloneargs[${#cloneargs[@]}]="--auth-key=$2"; shift 2;; + --mapped-uid) mapped_uid=$2; shift 2;; + --mapped-gid) mapped_gid=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +cloneargs=( "--name=$name" "${cloneargs[@]}" ) + +if [ $debug -eq 1 ]; then + set -x +fi + +if [ "$arch" = "i686" ]; then + arch=i386 +fi + +if [ "$skip_arch_check" = "0" ]; then + case "$hostarch:$arch" in + $arch:$arch) : ;; # the host == container + amd64:i386) :;; # supported "cross" + arm64:arm*) :;; # supported "cross" + armel:armhf) :;; # supported "cross" + armhf:armel) :;; # supported "cross" + *) echo "cannot create '$arch' container on hostarch '$hostarch'"; + exit 1;; + esac +fi + +if [ "$stream" != "daily" -a "$stream" != "released" -a "$stream" != "tryreleased" ]; then + echo "Only 'daily' and 'released' and 'tryreleased' streams are supported" + exit 1 +fi + +if [ -z "$path" ]; then + echo "'path' parameter is required" + exit 1 +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +# detect rootfs +config="$path/config" +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) + else + rootfs=$path/rootfs + fi +fi + +type ubuntu-cloudimg-query +type wget + +# determine the url, tarball, and directory names +# download if needed +# Allow the cache base to be set by environment variable +cache=${LXC_CACHE_PATH:-"$STATE_DIR/cache/lxc"}/cloud-$release +if [ $in_userns -eq 1 ]; then + STATE_DIR="$HOME/.cache/lxc" + cache=${LXC_CACHE_PATH:-"$STATE_DIR"}/cloud-$release +fi + +mkdir -p $cache + +if [ "$stream" = "tryreleased" ]; then + stream=released + ubuntu-cloudimg-query $release $stream $arch 1>/dev/null 2>/dev/null || stream=daily +fi + +if [ -n "$tarball" ]; then + url2="$tarball" +else + if ! url1=`ubuntu-cloudimg-query $release $stream $arch --format "%{url}\n"`; then + echo "There is no download available for release=$release, stream=$stream, arch=$arch" + [ "$stream" = "daily" ] || echo "You may try with '--stream=daily'" + exit 1 + fi + if [ "$release" = "precise" ] || [ "$release" = "trusty" ]; then + url2=`echo $url1 | sed -e 's/.tar.gz/-root\0/' -e 's/.tar.gz/.tar.xz/'` + else + url2=`echo $url1 | sed -e 's/.tar.gz/.squashfs/'` + fi +fi + +filename=`basename $url2` + +wgetcleanup() +{ + rm -f $filename +} + +do_extract_rootfs() { + + cd $cache + if [ $flushcache -eq 1 ]; then + echo "Clearing the cached images" + rm -f $filename + fi + + trap wgetcleanup EXIT SIGHUP SIGINT SIGTERM + if [ ! -f $filename ]; then + wget $url2 + fi + trap EXIT + trap SIGHUP + trap SIGINT + trap SIGTERM + + echo "Extracting container rootfs" + mkdir -p $rootfs + cd $rootfs + if [ "${filename##*.}" = "squashfs" ]; then + unsquashfs -n -f -d "$rootfs" "$cache/$filename" + else + if [ $in_userns -eq 1 ]; then + tar --anchored --exclude="dev/*" --numeric-owner -xpf "$cache/$filename" + mkdir -p $rootfs/dev/pts/ + else + tar --numeric-owner -xpf "$cache/$filename" + fi + fi +} + +if [ -n "$tarball" ]; then + do_extract_rootfs +else + mkdir -p "$STATE_DIR/lock/subsys/" + ( + flock -x 9 + do_extract_rootfs + ) 9>"$STATE_DIR/lock/subsys/lxc-ubuntu-cloud" +fi + +copy_configuration $path $rootfs $name $arch $release + +"$CLONE_HOOK_FN" "${cloneargs[@]}" "$rootfs" + +if [ $mapped_uid -ne -1 ]; then + chown $mapped_uid $path/config + chown -R $mapped_uid $STATE_DIR + chown -R $mapped_uid $cache +fi +if [ $mapped_gid -ne -1 ]; then + chgrp $mapped_gid $path/config + chgrp -R $mapped_gid $STATE_DIR + chgrp -R $mapped_gid $cache +fi + +echo "Container $name created." +exit 0 + +# vi: ts=4 expandtab diff --git a/templates/lxc-ubuntu.in b/templates/lxc-ubuntu.in new file mode 100644 index 0000000..80a28fd --- /dev/null +++ b/templates/lxc-ubuntu.in @@ -0,0 +1,877 @@ +#!/bin/bash + +# +# template script for generating ubuntu container for LXC +# +# This script consolidates and extends the existing lxc ubuntu scripts +# + +# Copyright © 2011 Serge Hallyn +# Copyright © 2010 Wilhelm Meier +# Author: Wilhelm Meier +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Detect use under userns (unsupported) +for arg in "$@"; do + [ "$arg" = "--" ] && break + if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then + echo "This template can't be used for unprivileged containers." 1>&2 + echo "You may want to try the \"download\" template instead." 1>&2 + exit 1 + fi +done + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +set -e + +LOCALSTATEDIR="@LOCALSTATEDIR@" +LXC_TEMPLATE_CONFIG="@LXCTEMPLATECONFIG@" +# Allows the lxc-cache directory to be set by environment variable +LXC_CACHE_PATH=${LXC_CACHE_PATH:-"$LOCALSTATEDIR/cache/lxc"} + +if [ -r /etc/default/lxc ]; then + . /etc/default/lxc +fi + +# Check if given path is in a btrfs partition +is_btrfs() +{ + [ -e $1 -a $(stat -f -c '%T' $1) = "btrfs" ] +} + +# Check if given path is the root of a btrfs subvolume +is_btrfs_subvolume() +{ + [ -d $1 -a $(stat -f -c '%T' $1) = "btrfs" -a $(stat -c '%i' $1) -eq 256 ] +} + +try_mksubvolume() +{ + path=$1 + [ -d $path ] && return 0 + mkdir -p $(dirname $path) + if which btrfs >/dev/null 2>&1 && is_btrfs $(dirname $path); then + btrfs subvolume create $path + else + mkdir -p $path + fi +} + +try_rmsubvolume() +{ + path=$1 + [ -d $path ] || return 0 + if which btrfs >/dev/null 2>&1 && is_btrfs_subvolume $path; then + btrfs subvolume delete $path + else + rm -rf $path + fi +} + +configure_ubuntu() +{ + rootfs=$1 + hostname=$2 + release=$3 + user=$4 + password=$5 + + # configure the network using the dhcp + if chroot $rootfs which netplan >/dev/null 2>&1; then + cat < $rootfs/etc/netplan/10-lxc.yaml +network: + ethernets: + eth0: {dhcp4: true} + version: 2 +EOF + else + cat < $rootfs/etc/network/interfaces +# This file describes the network interfaces available on your system +# and how to activate them. For more information, see interfaces(5). + +# The loopback network interface +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet dhcp +EOF + fi + + # set the hostname + cat < $rootfs/etc/hostname +$hostname +EOF + # set minimal hosts + cat < $rootfs/etc/hosts +127.0.0.1 localhost +127.0.1.1 $hostname + +# The following lines are desirable for IPv6 capable hosts +::1 ip6-localhost ip6-loopback +fe00::0 ip6-localnet +ff00::0 ip6-mcastprefix +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +EOF + + if [ ! -f $rootfs/etc/init/container-detect.conf ]; then + # suppress log level output for udev + sed -i "s/=\"err\"/=0/" $rootfs/etc/udev/udev.conf + + # remove jobs for consoles 5 and 6 since we only create 4 consoles in + # this template + rm -f $rootfs/etc/init/tty{5,6}.conf + fi + + if [ -z "$bindhome" ]; then + chroot $rootfs useradd --create-home -s /bin/bash $user + echo "$user:$password" | chroot $rootfs chpasswd + fi + + # make sure we have the current locale defined in the container + if [ -z "$LANG" ] || echo $LANG | grep -E -q "^C(\..+)*$"; then + chroot $rootfs locale-gen en_US.UTF-8 || true + chroot $rootfs update-locale LANG=en_US.UTF-8 || true + else + chroot $rootfs locale-gen $LANG || true + chroot $rootfs update-locale LANG=$LANG || true + fi + + # generate new SSH keys + if [ -x $rootfs/var/lib/dpkg/info/openssh-server.postinst ]; then + cat > $rootfs/usr/sbin/policy-rc.d << EOF +#!/bin/sh +exit 101 +EOF + chmod +x $rootfs/usr/sbin/policy-rc.d + + if [ -f "$rootfs/etc/init/ssh.conf" ]; then + mv "$rootfs/etc/init/ssh.conf" "$rootfs/etc/init/ssh.conf.disabled" + fi + + rm -f $rootfs/etc/ssh/ssh_host_*key* + + DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot $rootfs /var/lib/dpkg/info/openssh-server.postinst configure + + sed -i "s/root@$(hostname)/root@$hostname/g" $rootfs/etc/ssh/ssh_host_*.pub + + if [ -f "$rootfs/etc/init/ssh.conf.disabled" ]; then + mv "$rootfs/etc/init/ssh.conf.disabled" "$rootfs/etc/init/ssh.conf" + fi + + rm -f $rootfs/usr/sbin/policy-rc.d + fi + + return 0 +} + +# finish setting up the user in the container by injecting ssh key and +# adding sudo group membership. +# passed-in user is either 'ubuntu' or the user to bind in from host. +finalize_user() +{ + user=$1 + + sudo_version=$(chroot $rootfs dpkg-query -W -f='${Version}' sudo) + + if chroot $rootfs dpkg --compare-versions $sudo_version gt "1.8.3p1-1"; then + groups="sudo" + else + groups="sudo admin" + fi + + for group in $groups; do + chroot $rootfs groupadd --system $group >/dev/null 2>&1 || true + chroot $rootfs adduser ${user} $group >/dev/null 2>&1 || true + done + + if [ -n "$auth_key" -a -f "$auth_key" ]; then + u_path="/home/${user}/.ssh" + root_u_path="$rootfs/$u_path" + mkdir -p $root_u_path + cp $auth_key "$root_u_path/authorized_keys" + chroot $rootfs chown -R ${user}: "$u_path" + + echo "Inserted SSH public key from $auth_key into /home/${user}/.ssh/authorized_keys" + fi + return 0 +} + +# A function to try and autodetect squid-deb-proxy servers on the local network +# if either the squid-deb-proxy-client package is installed on the host or +# a parent container set the 50squid-deb-proxy-client file. +squid_deb_proxy_autodetect() +{ + local apt_discover=/usr/share/squid-deb-proxy-client/apt-avahi-discover + local proxy_file=/etc/apt/apt.conf.d/50squid-deb-proxy-client + squid_proxy_line= # That's a global :/ + + # Maybe the host is aware of a squid-deb-proxy? + if [ -f $apt_discover ]; then + echo -n "Discovering squid-deb-proxy..." + squid_proxy_line=$($apt_discover) + if [ -n "$squid_proxy_line" ]; then + echo "found squid-deb-proxy: $squid_proxy_line" + else + echo "no squid-deb-proxy found" + fi + fi + + # Are we in a nested container, and the parent already knows of a proxy? + if [ -f $proxy_file ]; then + # Extract the squid URL from the file (whatever is between "") + squid_proxy_line=`cat $proxy_file | sed "s/.*\"\(.*\)\".*/\1/"` + fi +} + +# +# Choose proxies for container +# http_proxy will be used by debootstrap on the host. +# APT_PROXY will be used to set /etc/apt/apt.conf.d/70proxy in the container. +# +choose_container_proxy() +{ + local rootfs=$1 + local arch=$2 + + if [ -z "$HTTP_PROXY" ]; then + HTTP_PROXY="none" + fi + case "$HTTP_PROXY" in + none) + squid_deb_proxy_autodetect + if [ -n "$squid_proxy_line" ]; then + APT_PROXY=$squid_proxy_line + export http_proxy=$squid_proxy_line + else + APT_PROXY= + fi + ;; + apt) + RES=`apt-config shell APT_PROXY Acquire::http::Proxy` + eval $RES + [ -z "$APT_PROXY" ] || export http_proxy=$APT_PROXY + ;; + *) + APT_PROXY=$HTTP_PROXY + export http_proxy=$HTTP_PROXY + ;; + esac +} + +write_sourceslist() +{ + # $1 => path to the partial cache or the rootfs + # $2 => architecture we want to add + # $3 => whether to use the multi-arch syntax or not + + if [ -n "$APT_PROXY" ]; then + mkdir -p $1/etc/apt/apt.conf.d + cat > $1/etc/apt/apt.conf.d/70proxy << EOF +Acquire::http::Proxy "$APT_PROXY" ; +EOF + fi + + case $2 in + amd64|i386) + MIRROR=${MIRROR:-http://archive.ubuntu.com/ubuntu} + SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.ubuntu.com/ubuntu} + ;; + *) + MIRROR=${MIRROR:-http://ports.ubuntu.com/ubuntu-ports} + SECURITY_MIRROR=${SECURITY_MIRROR:-http://ports.ubuntu.com/ubuntu-ports} + ;; + esac + if [ -n "$3" ]; then + cat >> "$1/etc/apt/sources.list" << EOF +deb [arch=$2] $MIRROR ${release} main restricted universe multiverse +deb [arch=$2] $MIRROR ${release}-updates main restricted universe multiverse +deb [arch=$2] $SECURITY_MIRROR ${release}-security main restricted universe multiverse +EOF + else + cat >> "$1/etc/apt/sources.list" << EOF +deb $MIRROR ${release} main restricted universe multiverse +deb $MIRROR ${release}-updates main restricted universe multiverse +deb $SECURITY_MIRROR ${release}-security main restricted universe multiverse +EOF + fi +} + +install_packages() +{ + local rootfs="$1" + shift + local packages="$*" + if [ -z $update ] + then + chroot $rootfs apt-get update + update=true + fi + if [ -n "${packages}" ] + then + chroot $rootfs apt-get install --force-yes -y --no-install-recommends ${packages} + fi +} + +cleanup() +{ + try_rmsubvolume $cache/partial-$arch + try_rmsubvolume $cache/rootfs-$arch +} + +suggest_flush() +{ + echo "Container upgrade failed. The container cache may be out of date," + echo "in which case flushing the cache (see -F in the help output) may help." +} + +download_ubuntu() +{ + cache=$1 + arch=$2 + release=$3 + + case $2 in + amd64|i386) + MIRROR=${MIRROR:-http://archive.ubuntu.com/ubuntu} + SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.ubuntu.com/ubuntu} + ;; + *) + MIRROR=${MIRROR:-http://ports.ubuntu.com/ubuntu-ports} + SECURITY_MIRROR=${SECURITY_MIRROR:-http://ports.ubuntu.com/ubuntu-ports} + ;; + esac + + packages_template=${packages_template:-"apt-transport-https,ssh,vim"} + debootstrap_parameters= + + # Try to guess a list of langpacks to install + langpacks="language-pack-en" + + if which dpkg >/dev/null 2>&1; then + langpacks=`(echo $langpacks && + dpkg -l | grep -E "^ii language-pack-[a-z]* " | + cut -d ' ' -f3) | sort -u` + fi + packages_template="${packages_template},$(echo $langpacks | sed 's/ /,/g')" + + if [ -n "$variant" ]; then + debootstrap_parameters="$debootstrap_parameters --variant=$variant" + fi + if [ "$variant" = 'minbase' ]; then + packages_template="${packages_template},sudo" + # Newer releases use netplan, EOL releases not supported + case $release in + trusty|xenial|zesty) + packages_template="${packages_template},ifupdown,isc-dhcp-client" + ;; + esac + fi + + echo "Installing packages in template: ${packages_template}" + + trap cleanup EXIT SIGHUP SIGINT SIGTERM + # check the mini ubuntu was not already downloaded + try_mksubvolume "$cache/partial-$arch" + if [ $? -ne 0 ]; then + echo "Failed to create '$cache/partial-$arch' directory" + return 1 + fi + + choose_container_proxy $cache/partial-$arch/ $arch + # download a mini ubuntu into a cache + echo "Downloading ubuntu $release minimal ..." + if [ -n "$(which qemu-debootstrap)" ]; then + qemu-debootstrap --verbose $debootstrap_parameters --components=main,universe --arch=$arch --include=${packages_template} $release $cache/partial-$arch $MIRROR + else + debootstrap --verbose $debootstrap_parameters --components=main,universe --arch=$arch --include=${packages_template} $release $cache/partial-$arch $MIRROR + fi + + if [ $? -ne 0 ]; then + echo "Failed to download the rootfs, aborting." + return 1 + fi + + # Serge isn't sure whether we should avoid doing this when + # $release == `distro-info -d` + echo "Installing updates" + > $cache/partial-$arch/etc/apt/sources.list + write_sourceslist $cache/partial-$arch/ $arch + + chroot "$1/partial-${arch}" apt-get update + if [ $? -ne 0 ]; then + echo "Failed to update the apt cache" + return 1 + fi + cat > "$1/partial-${arch}"/usr/sbin/policy-rc.d << EOF +#!/bin/sh +exit 101 +EOF + chmod +x "$1/partial-${arch}"/usr/sbin/policy-rc.d + + ( + cat << EOF + mount -t proc proc "${1}/partial-${arch}/proc" + chroot "${1}/partial-${arch}" apt-get dist-upgrade -y +EOF + ) | lxc-unshare -s MOUNT -- sh -eu || (suggest_flush; false) + + rm -f "$1/partial-${arch}"/usr/sbin/policy-rc.d + + chroot "$1/partial-${arch}" apt-get clean + + mv "$1/partial-$arch" "$1/rootfs-$arch" + trap EXIT + trap SIGINT + trap SIGTERM + trap SIGHUP + echo "Download complete" + return 0 +} + +copy_ubuntu() +{ + cache=$1 + arch=$2 + rootfs=$3 + + # make a local copy of the miniubuntu + echo "Copying rootfs to $rootfs ..." + try_mksubvolume $rootfs + if which btrfs >/dev/null 2>&1 && is_btrfs_subvolume $cache/rootfs-$arch && is_btrfs_subvolume $rootfs; then + realrootfs=$(dirname $config)/rootfs + [ "$rootfs" = "$realrootfs" ] || umount $rootfs || return 1 + btrfs subvolume delete $realrootfs || return 1 + btrfs subvolume snapshot $cache/rootfs-$arch $realrootfs || return 1 + [ "$rootfs" = "$realrootfs" ] || mount --bind $realrootfs $rootfs || return 1 + else + rsync -SHaAX $cache/rootfs-$arch/ $rootfs/ || return 1 + fi + return 0 +} + +install_ubuntu() +{ + rootfs=$1 + release=$2 + flushcache=$3 + cache="$4/$release" + mkdir -p $LOCALSTATEDIR/lock/subsys/ + + ( + flock -x 9 + if [ $? -ne 0 ]; then + echo "Cache repository is busy." + return 1 + fi + + + if [ $flushcache -eq 1 ]; then + echo "Flushing cache..." + try_rmsubvolume $cache/partial-$arch + try_rmsubvolume $cache/rootfs-$arch + fi + + echo "Checking cache download in $cache/rootfs-$arch ... " + if [ ! -e "$cache/rootfs-$arch" ]; then + download_ubuntu $cache $arch $release + if [ $? -ne 0 ]; then + echo "Failed to download 'ubuntu $release base'" + return 1 + fi + fi + + echo "Copy $cache/rootfs-$arch to $rootfs ... " + copy_ubuntu $cache $arch $rootfs + if [ $? -ne 0 ]; then + echo "Failed to copy rootfs" + return 1 + fi + + return 0 + + ) 9>$LOCALSTATEDIR/lock/subsys/lxc-ubuntu$release + + return $? +} + +copy_configuration() +{ + path=$1 + rootfs=$2 + name=$3 + arch=$4 + release=$5 + + if [ $arch = "i386" ]; then + arch="i686" + fi + + # if there is exactly one veth network entry, make sure it has an + # associated hwaddr. + nics=`grep -e '^lxc\.net\.0\.type[ \t]*=[ \t]*veth' $path/config | wc -l` + if [ $nics -eq 1 ]; then + grep -q "^lxc.net.0.hwaddr" $path/config || sed -i -e "/^lxc\.net\.0\.type[ \t]*=[ \t]*veth/a lxc.net.0.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config + fi + + # Generate the configuration file + ## Relocate all the network config entries + sed -i -e "/lxc.net.0/{w ${path}/config-network" -e "d}" $path/config + + ## Relocate any other config entries + sed -i -e "/lxc./{w ${path}/config-auto" -e "d}" $path/config + + ## Add all the includes + echo "" >> $path/config + echo "# Common configuration" >> $path/config + if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu.common.conf" ]; then + echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu.common.conf" >> $path/config + fi + if [ -e "${LXC_TEMPLATE_CONFIG}/ubuntu.${release}.conf" ]; then + echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/ubuntu.${release}.conf" >> $path/config + fi + + ## Add the container-specific config + echo "" >> $path/config + echo "# Container specific configuration" >> $path/config + [ -e "$path/config-auto" ] && cat $path/config-auto >> $path/config && rm $path/config-auto + grep -q "^lxc.rootfs.path" $path/config 2>/dev/null || echo "lxc.rootfs.path = $rootfs" >> $path/config + cat <> $path/config +lxc.uts.name = $name +lxc.arch = $arch +EOF + + ## Re-add the previously removed network config + echo "" >> $path/config + echo "# Network configuration" >> $path/config + cat $path/config-network >> $path/config + rm $path/config-network + + if [ $? -ne 0 ]; then + echo "Failed to add configuration" + return 1 + fi + + return 0 +} + +post_process() +{ + rootfs=$1 + release=$2 + packages=$3 + + # Disable service startup + cat > $rootfs/usr/sbin/policy-rc.d << EOF +#!/bin/sh +exit 101 +EOF + chmod +x $rootfs/usr/sbin/policy-rc.d + + # If the container isn't running a native architecture, setup multiarch + if [ -x "$(ls -1 ${rootfs}/usr/bin/qemu-*-static 2>/dev/null)" ]; then + dpkg_version=$(chroot $rootfs dpkg-query -W -f='${Version}' dpkg) + if chroot $rootfs dpkg --compare-versions $dpkg_version ge "1.16.2"; then + chroot $rootfs dpkg --add-architecture ${hostarch} + else + mkdir -p ${rootfs}/etc/dpkg/dpkg.cfg.d + echo "foreign-architecture ${hostarch}" > ${rootfs}/etc/dpkg/dpkg.cfg.d/lxc-multiarch + fi + + # Save existing value of MIRROR and SECURITY_MIRROR + DEFAULT_MIRROR=$MIRROR + DEFAULT_SECURITY_MIRROR=$SECURITY_MIRROR + + # Write a new sources.list containing both native and multiarch entries + > ${rootfs}/etc/apt/sources.list + write_sourceslist $rootfs $arch "native" + + MIRROR=$DEFAULT_MIRROR + SECURITY_MIRROR=$DEFAULT_SECURITY_MIRROR + write_sourceslist $rootfs $hostarch "multiarch" + + # Finally update the lists and install upstart using the host architecture + HOST_PACKAGES="upstart:${hostarch} mountall:${hostarch} isc-dhcp-client:${hostarch}" + chroot $rootfs apt-get update + if chroot $rootfs dpkg -l iproute2 | grep -q ^ii; then + HOST_PACKAGES="$HOST_PACKAGES iproute2:${hostarch}" + else + HOST_PACKAGES="$HOST_PACKAGES iproute:${hostarch}" + fi + install_packages $rootfs $HOST_PACKAGES + fi + + # Install Packages in container + if [ -n "$packages" ] + then + local packages="`echo $packages | sed 's/,/ /g'`" + echo "Installing packages: ${packages}" + install_packages $rootfs $packages + fi + + # Set initial timezone as on host + if [ -f /etc/timezone ]; then + cat /etc/timezone > $rootfs/etc/timezone + chroot $rootfs dpkg-reconfigure -f noninteractive tzdata + elif [ -f /etc/sysconfig/clock ]; then + . /etc/sysconfig/clock + echo $ZONE > $rootfs/etc/timezone + chroot $rootfs dpkg-reconfigure -f noninteractive tzdata + else + echo "Timezone in container is not configured. Adjust it manually." + fi + + # rmdir /dev/shm for containers that have /run/shm + # I'm afraid of doing rm -rf $rootfs/dev/shm, in case it did + # get bind mounted to the host's /run/shm. So try to rmdir + # it, and in case that fails move it out of the way. + # NOTE: This can only be removed once 12.04 goes out of support + if [ ! -L $rootfs/dev/shm ] && [ -e $rootfs/dev/shm ]; then + rmdir $rootfs/dev/shm 2>/dev/null || mv $rootfs/dev/shm $rootfs/dev/shm.bak + ln -s /run/shm $rootfs/dev/shm + fi + + # Re-enable service startup + rm $rootfs/usr/sbin/policy-rc.d +} + +do_bindhome() +{ + rootfs=$1 + user=$2 + + # copy /etc/passwd, /etc/shadow, and /etc/group entries into container + pwd=`getent passwd $user` || { echo "Failed to copy password entry for $user"; false; } + echo $pwd >> $rootfs/etc/passwd + + # make sure user's shell exists in the container + shell=`echo $pwd | cut -d: -f 7` + if [ ! -x $rootfs/$shell ]; then + echo "shell $shell for user $user was not found in the container." + pkg=`dpkg -S $(readlink -m $shell) | cut -d ':' -f1` + echo "Installing $pkg" + install_packages $rootfs $pkg + fi + + shad=`getent shadow $user` + echo "$shad" >> $rootfs/etc/shadow + + # bind-mount the user's path into the container's /home + h=`getent passwd $user | cut -d: -f 6` + mkdir -p $rootfs/$h + + # use relative path in container + h2=${h#/} + while [ ${h2:0:1} = "/" ]; do + h2=${h2#/} + done + echo "lxc.mount.entry = $h $h2 none bind 0 0" >> $path/config + + # Make sure the group exists in container + grp=`echo $pwd | cut -d: -f 4` # group number for $user + grpe=`getent group $grp` || return 0 # if host doesn't define grp, ignore in container + chroot $rootfs getent group "$grpe" || echo "$grpe" >> $rootfs/etc/group +} + +usage() +{ + cat <] [-d|--debug] + [-F | --flush-cache] [-r|--release ] [-v|--variant] [ -S | --auth-key ] + [--rootfs ] [--packages ] [-u|--user ] [--password ] + [--mirror ] [--security-mirror ] +release: the ubuntu release (e.g. xenial): defaults to host release on ubuntu, otherwise uses latest LTS +variant: debootstrap variant to use (see debootstrap(8)) +bindhome: bind 's home into the container + The ubuntu user will not be created, and will have + sudo access. +arch: the container architecture (e.g. amd64): defaults to host arch +auth-key: SSH Public key file to inject into container +packages: list of packages to add comma separated +mirror,security-mirror: mirror for download and /etc/apt/sources.list +EOF + return 0 +} + +options=$(getopt -o a:b:hp:r:v:n:FS:du: -l arch:,bindhome:,help,path:,release:,variant:,name:,flush-cache,auth-key:,debug,rootfs:,packages:,user:,password:,mirror:,security-mirror: -- "$@") +if [ $? -ne 0 ]; then + usage $(basename $0) + exit 1 +fi +eval set -- "$options" + +release=xenial # Default to the last Ubuntu LTS release for non-Ubuntu systems +if [ -f /etc/lsb-release ]; then + . /etc/lsb-release + if [ "$DISTRIB_ID" = "Ubuntu" ]; then + release=$DISTRIB_CODENAME + fi +fi + +bindhome= + +# Code taken from debootstrap +if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then + arch=`/usr/bin/dpkg --print-architecture` +elif which udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then + arch=`/usr/bin/udpkg --print-architecture` +else + arch=$(uname -m) + if [ "$arch" = "i686" ]; then + arch="i386" + elif [ "$arch" = "x86_64" ]; then + arch="amd64" + elif [ "$arch" = "armv7l" ]; then + arch="armhf" + elif [ "$arch" = "aarch64" ]; then + arch="arm64" + elif [ "$arch" = "ppc64le" ]; then + arch="ppc64el" + fi +fi + +debug=0 +hostarch=$arch +flushcache=0 +packages="" +user="ubuntu" +password="ubuntu" + +while true +do + case "$1" in + -h|--help) usage $0 && exit 0;; + --rootfs) rootfs=$2; shift 2;; + -p|--path) path=$2; shift 2;; + -n|--name) name=$2; shift 2;; + -u|--user) user=$2; shift 2;; + --password) password=$2; shift 2;; + -F|--flush-cache) flushcache=1; shift 1;; + -r|--release) release=$2; shift 2;; + -v|--variant) variant=$2; shift 2;; + --packages) packages=$2; shift 2;; + -b|--bindhome) bindhome=$2; shift 2;; + -a|--arch) arch=$2; shift 2;; + -S|--auth-key) auth_key=$2; shift 2;; + -d|--debug) debug=1; shift 1;; + --mirror) MIRROR=$2; shift 2;; + --security-mirror) SECURITY_MIRROR=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ $debug -eq 1 ]; then + set -x +fi + +if [ -n "$bindhome" ]; then + pwd=`getent passwd $bindhome` + if [ $? -ne 0 ]; then + echo "Error: no password entry found for $bindhome" + exit 1 + fi +fi + + +if [ "$arch" = "i686" ]; then + arch=i386 +fi + +if [ $hostarch = "i386" -a $arch = "amd64" ]; then + echo "can't create $arch container on $hostarch" + exit 1 +fi + +if [ $hostarch = "armhf" -o $hostarch = "armel" -o $hostarch = "arm64" ] && \ + [ $arch != "armhf" -a $arch != "armel" -a $arch != "arm64" ]; then + echo "can't create $arch container on $hostarch" + exit 1 +fi + +if [ $arch = "arm64" ] && [ $hostarch != "arm64" ]; then + echo "can't create $arch container on $hostarch" + exit 1 +fi + +if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then + echo "can't create $arch container on $hostarch" + exit 1 +fi + +which debootstrap >/dev/null 2>&1 || { echo "'debootstrap' command is missing" >&2; false; } + +if [ -z "$path" ]; then + echo "'path' parameter is required" + exit 1 +fi + +if [ "$(id -u)" != "0" ]; then + echo "This script should be run as 'root'" + exit 1 +fi + +# detect rootfs +config="$path/config" +# if $rootfs exists here, it was passed in with --rootfs +if [ -z "$rootfs" ]; then + if grep -q '^lxc.rootfs.path' $config 2>/dev/null ; then + rootfs=$(awk -F= '/^lxc.rootfs.path =/{ print $2 }' $config) + else + rootfs=$path/rootfs + fi +fi + +install_ubuntu $rootfs $release $flushcache $LXC_CACHE_PATH +if [ $? -ne 0 ]; then + echo "failed to install ubuntu $release" + exit 1 +fi + +configure_ubuntu $rootfs $name $release $user $password +if [ $? -ne 0 ]; then + echo "failed to configure ubuntu $release for a container" + exit 1 +fi + +copy_configuration $path $rootfs $name $arch $release +if [ $? -ne 0 ]; then + echo "failed write configuration file" + exit 1 +fi + +post_process $rootfs $release $trim_container $packages + +if [ -n "$bindhome" ]; then + do_bindhome $rootfs $bindhome + finalize_user $bindhome +else + finalize_user $user +fi + +echo "" +echo "##" +if [ -n "$bindhome" ]; then + echo "# Log in as user $bindhome" +else + echo "# The default user is '$user' with password '$password'!" + echo "# Use the 'sudo' command to run tasks as root in the container." +fi +echo "##" +echo "" diff --git a/templates/lxc-voidlinux.in b/templates/lxc-voidlinux.in new file mode 100644 index 0000000..d74da38 --- /dev/null +++ b/templates/lxc-voidlinux.in @@ -0,0 +1,199 @@ +#!/bin/bash + +# +# template script for generating Void Linux container for LXC +# + +# +# lxc: linux Container library + +# Authors: +# Gregor Reitzenstein + +# Based on lxc-archlinux template by: +# Alexander Vladimirov +# John Lane + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library 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 +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Utility functions + +# Check if array $2 contains item $1 +containsElement() { + local e + for e in "${@:2}"; do [[ "$1" == "$e" ]] && return 0; done + return 1 +} + +# split comma-separated string into an array +# ${1} - string to split +# ${2} - separator (default is ",") +# ${result} - result value on success +split_string() { + local ifs=${IFS} + IFS="${2:-,}" + read -ra result < <(echo "${1}") + IFS=${ifs} + return 0 +} + +# Make sure the usual locations are in PATH +export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin + +# defaults +default_path="/var/lib/lxc" +default_path="@LXCPATH@" +shared_config="@LXCTEMPLATECONFIG@/voidlinux.common.conf" +userns_config="@LXCTEMPLATECONFIG@/voidlinux.userns.conf" + +pkg_blacklist=("linux>=0" "e2fsprogs>=0" "btrfs-progs>=0" "xfsprogs>=0" "f2fs-tools>=0" "dosfstools>=0") +base_packages=() +for pkg in $(xbps-query -Mv --repository="http://repo2.voidlinux.eu/current/" -x base-system); do + containsElement "$pkg" "${pkg_blacklist[@]}" || base_packages+=($pkg) +done +declare -a additional_packages + +copy_configuration() { + mkdir -p "${config_path}" + local config="${config_path}/config" + echo "lxc.uts.name = ${name}" >> "${config}" + grep -q "^lxc.rootfs.path" "${config}" 2>/dev/null \ + || echo "lxc.rootfs.path = ${rootfs_path}" >> "${config}" + + # Detect if were in a UserNS and include the right config + if [ -z "${LXC_MAPPED_GID+x}" ] || [ -z "${LXC_MAPPED_UID+x}" ]; then + echo "lxc.include = ${userns_config}" >> "${config}" + else + echo "lxc.include = ${shared_config}" >> "${config}" + fi + + if [ $? -ne 0 ]; then + echo "Failed to configure container" + return 1 + fi + return 0 +} + +install_void() { + if ! yes | xbps-install -Sy -R http://repo2.voidlinux.eu/current -r "${rootfs_path}" "${base_packages[@]}" + then + echo "Failed to install container packages" + return 1 + fi +} + +usage() { + cat < [-p|--path=] [-a|--arch=] + [-r|--root_password=] [-P|--packages=] [-h|--help] + +Mandatory args: + -n,--name container name, used to as an identifier for that container from now on +Optional args: + -p,--path path to where the container rootfs will be created (${default_path}) + --rootfs path for actual container rootfs, (${default_path}/rootfs) + -P,--packages preinstall additional packages, comma-separated list + -c,--config use specified pacman config when installing container packages + -a,--arch use specified architecture instead of host's architecture + -r,--root_password set container root password + -h,--help print this help +EOF + return 0 +} + +options=$(getopt -o hp:P:n:c:r: -l help,rootfs:,path:,packages:,name:,config:,root_password:,mapped-uid:,mapped-gid: -- "${@}") +if [ ${?} -ne 0 ]; then + usage "$(basename "${0}")" + exit 1 +fi +eval set -- "${options}" + +while true +do + case "${1}" in + -h|--help) usage "${0}" && exit 0;; + -p|--path) path=${2}; shift 2;; + -n|--name) name=${2}; shift 2;; + -c|--config) config_path=${2}; shift 2;; + --rootfs) rootfs_path=${2}; shift 2;; + -P|--packages) additional_packages=${2}; shift 2;; + -r|--root_password) root_passwd=${2}; shift 2;; + --mapped-uid) LXC_MAPPED_UID=$2; shift 2;; + --mapped-gid) LXC_MAPPED_GID=$2; shift 2;; + --) shift 1; break ;; + *) break ;; + esac +done + +if [ -z "${name}" ]; then + echo "missing required 'name' parameter" + exit 1 +fi + +type xbps-install >/dev/null 2>&1 +if [ ${?} -ne 0 ]; then + echo "'xbps-install' command is missing." +fi +type xbps-query >/dev/null 2>&1 +if [ ${?} -ne 0 ]; then + echo "'xbps-query' command is missing." +fi + +if [ -z "${rootfs_path}" ]; then + rootfs_path="${path}/rootfs" +fi +config_path="${path}" + +revert() { + echo "Interrupted, cleaning up" + lxc-destroy -n "${name}" + rm -rf "${path:?}/${name}" + rm -rf "${default_path:?}/${name}" + exit 1 +} +trap revert SIGHUP SIGINT SIGTERM + +copy_configuration +if [ $? -ne 0 ]; then + echo "Failed to write configuration file" + rm -rf "${config_path}" + exit 1 +fi + +if [ ${#additional_packages[@]} -gt 0 ]; then + split_string "${additional_packages}" + base_packages+=(${result[@]}) +fi + +mkdir -p "${rootfs_path}" +install_void +if [ ${?} -ne 0 ]; then + echo "Failed to install Void Linux" + rm -rf "${config_path}" "${path}" + exit 1 +fi + + + +if [ -n "${root_passwd}" ]; then + echo "root:${root_passwd}" | chroot "${rootfs_path}" chpasswd +fi + +cat << EOF +Void Linux Container ${name} has been successfully created. The configuration is +stored in ${config_path}/config. Please refer to https://wiki.voidlinux.eu for +information regarding Void Linux. +EOF