Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:mbussolotto:branches:systemsmanagement:Uyuni:Master
spacewalk-certs-tools
spacewalk-certs-tools-git-631.98f5f07.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File spacewalk-certs-tools-git-631.98f5f07.obscpio of Package spacewalk-certs-tools
07070100000000000041ED000003E80000006400000002662798DF00000000000000000000000000000000000000000000001600000000spacewalk-certs-tools07070100000001000081A4000003E80000006400000001662798DF000046AC000000000000000000000000000000000000001E00000000spacewalk-certs-tools/LICENSE GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. 07070100000002000081A4000003E80000006400000001662798DF000007F3000000000000000000000000000000000000002500000000spacewalk-certs-tools/Makefile.certs# # Copyright (c) 2008--2015 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # Makefile for the backend directory # SUBDIR = certs FILES = __init__ rhn_ssl_tool sslToolCli sslToolConfig sslToolLib \ timeLib rhn_bootstrap rhn_bootstrap_strings \ mgr_ssl_cert_setup INSTALL_ROOT_FILES = gen-rpm.sh sign.sh update-ca-cert-trust.sh # check if we can build man pages DOCBOOK = $(wildcard /usr/bin/docbook2man) MANSECT = 1 SGMLS = $(wildcard *.sgml) MANS = $(patsubst %.sgml,%.$(MANSECT),$(SGMLS)) BINDIR = /usr/bin SBINDIR = /usr/sbin MANDIR ?= /usr/share/man PUB_BOOTSTRAP_DIR = /srv/www/htdocs/pub/bootstrap EXTRA_DIRS = $(MANDIR)/man$(MANSECT) $(BINDIR) $(PUB_BOOTSTRAP_DIR) $(SBINDIR) # now include some Macros include Makefile.defs SBINFILES = mgr-package-rpm-certificate-osimage BINFILES = rhn-sudo-ssl-tool PYBINFILES = rhn-ssl-tool rhn-bootstrap mgr-ssl-cert-setup install :: $(SBINFILES) $(BINFILES) $(PYBINFILES) $(MANS) $(PREFIX)/$(MANDIR) $(INSTALL_DATA) $(MANS) $(PREFIX)$(MANDIR)/man$(MANSECT) $(foreach f,$(SBINFILES), \ $(INSTALL_BIN) $(f) $(PREFIX)$(SBINDIR)/$(f) ; ) $(foreach f,$(BINFILES), \ $(INSTALL_BIN) $(f) $(PREFIX)$(BINDIR)/$(f) ; ) $(foreach f,$(PYBINFILES), \ $(INSTALL_BIN) $(f) $(PREFIX)$(BINDIR)/$(f)-$(PYTHONVERSION) ; ) @$(foreach f,$(INSTALL_ROOT_FILES), \ $(INSTALL_DATA) $(f) $(PREFIX)$(ROOT)/$(SUBDIR)/$(f) ; ) %.$(MANSECT) : %.sgml /usr/bin/docbook2man $< clean :: @rm -fv $(BINFILES).new $(MANS) manpage.* 07070100000003000081A4000003E80000006400000001662798DF00000995000000000000000000000000000000000000002400000000spacewalk-certs-tools/Makefile.defs# # Copyright (c) 2008--2013 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # Common pathnames and programs for the Spacewalk project # # if not defined, definit as a noop TOP ?= . # global defines which control this build and where we deploy files ROOT ?= /usr/share/rhn export ROOT PREFIX ?= export PREFIX # Compilation stuff CC = gcc PYTHON_INCLUDE = -I/usr/include/python$(PythonVersion) CFLAGS = -Wall -O2 -fomit-frame-pointer $(PYTHON_INCLUDE) -fPIC SOFLAGS = -shared -fPIC # Installation stuff INSTALL = /usr/bin/install -c --verbose INSTALL_BIN = $(INSTALL) -m 755 INSTALL_DATA = $(INSTALL) -m 644 INSTALL_DIR = $(INSTALL) -m 755 -d # This is for the subdir part PYFILES = $(addsuffix .py,$(FILES)) # what do we need to install and where INSTALL_FILES += $(PYFILES) INSTALL_DEST ?= $(PYTHONPATH)/$(SUBDIR) DIRS += $(addprefix $(PREFIX), \ $(sort $(EXTRA_DIRS)) $(INSTALL_DEST)) all :: $(INSTALL_FILES) install :: all $(DIRS) $(INSTALL_FILES) @$(foreach f,$(INSTALL_FILES), \ $(INSTALL_DATA) $(f) $(PREFIX)$(INSTALL_DEST)/$(f) ; ) $(DIRS): $(INSTALL_DIR) $@ clean :: @rm -fv *~ *.pyc *.pyo .??*~ @rm -fv .\#* @rm -fv core # useful macro descend-subdirs = @$(foreach d,$(SUBDIRS), $(MAKE) -C $(d) $@ || exit 1; ) # subdirs are treated at the end all install clean:: $(SUBDIRS) $(descend-subdirs) # extra toy targets # Python checker support PYTHONPATH = $(TOP) PYCHECKER = pychecker PYCHECKEROPTS = --maxreturns 10 --maxbranches 15 DBCHECKER = db-checker.py DBCHECKEROPTS = DB = user/pass@instance pychecker :: $(PYFILES) @PYTHONPATH=$(PYTHONPATH) $(PYCHECKER) $(PYCHECKEROPTS) $(PYFILES) || : $(descend-subdirs) db-checker :: $(PYFILES) @PYTHONPATH=$(PYTHONPATH) $(TOP)/$(DBCHECKER) $(DBCHECKEROPTS) $(PYFILES) || : $(descend-subdirs) graphviz :: @PYTHONPATH=$(PYTHONPATH) $(PYCHECKER) -Z $(PYCHECKEROPTS) $(PYFILES) || exit 0 07070100000004000081A4000003E80000006400000001662798DF0000033E000000000000000000000000000000000000002600000000spacewalk-certs-tools/Makefile.pythonTHIS_MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST))) CURRENT_DIR := $(dir $(THIS_MAKEFILE)) include $(CURRENT_DIR)../../rel-eng/Makefile.python # Docker tests variables DOCKER_CONTAINER_BASE = uyuni-master DOCKER_REGISTRY = registry.mgr.suse.de DOCKER_RUN_EXPORT = "PYTHONPATH=$PYTHONPATH" DOCKER_VOLUMES = -v "$(CURDIR)/../../:/manager" __pylint :: $(call update_pip_env) pylint --rcfile=pylintrc $(shell find -name '*.py') > reports/pylint.log || true docker_pylint :: docker run --rm -e $(DOCKER_RUN_EXPORT) $(DOCKER_VOLUMES) $(DOCKER_REGISTRY)/$(DOCKER_CONTAINER_BASE)-pgsql /bin/sh -c "cd /manager/spacewalk/certs-tools; make -f Makefile.python __pylint" docker_shell :: docker run -t -i --rm -e $(DOCKER_RUN_EXPORT) $(DOCKER_VOLUMES) $(DOCKER_REGISTRY)/$(DOCKER_CONTAINER_BASE)-pgsql /bin/bash 07070100000005000081A4000003E80000006400000001662798DF000002AD000000000000000000000000000000000000002200000000spacewalk-certs-tools/__init__.py# pylint: disable=missing-module-docstring,invalid-name # # Copyright (c) 2008--2013 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # __all__ = [] 07070100000006000081ED000003E80000006400000001662798DF00002A30000000000000000000000000000000000000002100000000spacewalk-certs-tools/gen-rpm.sh#!/bin/bash # # Copyright (c) 2008--2018 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # Builds an rpm out of command line options # PATH=$PATH:/usr/bin NAME=useless EPOCH= VERSION=1.0.0 RELEASE=1 ARCH=noarch GROUP="RHN/Dummy" SUMMARY="Dummy package" DESCRIPTION="The package description" PACKAGER="(none)" VENDOR="(none)" usage() { echo "Usage:" echo "$0 [OPTIONS] [FILES]" echo "Builds an rpm from the specified command line options, optionally" echo "including files" echo echo " OPTIONS:" echo " --help display usage and exit" echo " --name use this name for the rpm [$NAME]" echo " --epoch use this epoch [default is null]" echo " --version use this version [$VERSION]" echo " --release use this release [$RELEASE]" echo " --arch build the package for this arch [$ARCH]" echo " --group group this package belongs [$GROUP]" echo " --packager packager [$PACKAGER]" echo " --vendor vendor [$VENDOR]" echo " --summary package summary" echo " --description package description" echo " --requires make the generated rpm require this" echo " --provides make the generated rpm provide this" echo " --conflicts make the generated rpm conflict with this" echo " --obsoletes make the generated rpm obsolete with this" echo " --buildreq make the generated source rpm require this to build" echo " --post path to a postinstall script" echo " --pre path to a preinstall script" echo " --postun path to a post-uninstall script" echo " --preun path to a pre-uninstall script" echo echo " FILES: <file-spec>*" echo " <file-spec> ::== <dest-path>[:mode]=<source-path>" echo " adds <source-path> in the RPM package, at <dest-path>" echo " NOTE: <dest-path> has to be a filename" echo echo "Example:" echo " $0 --name useless --version 0.99 --release 1 /var/foo=/var/log/messages" exit $1 } ensure_file_exists() { file=$1 sectname=$2 if [ "$file" -a ! -s "$file" ]; then echo "Cannot read $sectname section from $file" exit -1 fi } # Generates the post/pre/postun/preun sections generate_section() { file=$1 sectname=$2 if [ "$file" ]; then echo "%$sectname" cat $file echo fi } # Builds the requires/provides/conflicts/obsoletes generate_depend_section() { sectname=$1 shift while [ "$1" ]; do val=$1 shift echo "$sectname: $val" done | sort | uniq } # Array we use for grabbing the files declare -a PARAMS declare -a REQUIRES declare -a PROVIDES declare -a CONFLICTS declare -a OBSOLETES declare -a BUILDREQS POST="" PRE="" POSTUN="" PREUN="" while true; do case "$1" in --help) usage 0 ;; --name) case "$2" in "") echo "No parameter specified for --name"; break;; *) NAME=$2; shift 2;; esac;; --epoch) case "$2" in "") echo "No parameter specified for --epoch"; break;; *) EPOCH=$2; shift 2;; esac;; --version) case "$2" in "") echo "No parameter specified for --version"; break;; *) VERSION=$2; shift 2;; esac;; --release) case "$2" in "") echo "No parameter specified for --release"; break;; *) RELEASE=$2; shift 2;; esac;; --arch) case "$2" in "") echo "No parameter specified for --arch"; break;; *) ARCH=$2; shift 2;; esac;; --group) case "$2" in "") echo "No parameter specified for --group"; break;; *) GROUP=$2; shift 2;; esac;; --summary) case "$2" in "") echo "No parameter specified for --summary"; break;; *) SUMMARY=$2; shift 2;; esac;; --description) case "$2" in "") echo "No parameter specified for --description"; break;; *) DESCRIPTION=$2; shift 2;; esac;; --packager) case "$2" in "") echo "No parameter specified for --packager"; break;; *) PACKAGER=$2; shift 2;; esac;; --vendor) case "$2" in "") echo "No parameter specified for --vendor"; break;; *) VENDOR=$2; shift 2;; esac;; --post) case "$2" in "") echo "No parameter specified for --post"; break;; *) POST=$2; shift 2;; esac;; --pre) case "$2" in "") echo "No parameter specified for --pre"; break;; *) PRE=$2; shift 2;; esac;; --postun) case "$2" in "") echo "No parameter specified for --postun"; break;; *) POSTUN=$2; shift 2;; esac;; --preun) case "$2" in "") echo "No parameter specified for --preun"; break;; *) PREUN=$2; shift 2;; esac;; --requires) case "$2" in "") echo "No parameter specified for --requires"; break;; *) REQUIRES[${#REQUIRES[*]}]=$2; shift 2;; esac;; --provides) case "$2" in "") echo "No parameter specified for --provides"; break;; *) PROVIDES[${#PROVIDES[*]}]=$2; shift 2;; esac;; --conflicts) case "$2" in "") echo "No parameter specified for --conflicts"; break;; *) CONFLICTS[${#CONFLICTS[*]}]=$2; shift 2;; esac;; --obsoletes) case "$2" in "") echo "No parameter specified for --obsoletes"; break;; *) OBSOLETES[${#OBSOLETES[*]}]=$2; shift 2;; esac;; --buildreq) case "$2" in "") echo "No parameter specified for --buildreq"; break;; *) BUILDREQ[${#BUILDREQ[*]}]=$2; shift 2;; esac;; "") break;; *) ftwo=`echo $1 | (read -n 2 foo; echo $foo)` if [ $ftwo == '--' ]; then echo "Unknown option $1" usage 1 fi PARAMS[${#PARAMS[*]}]=$1 shift esac done ensure_file_exists "$PRE" "pre" ensure_file_exists "$POST" "post" ensure_file_exists "$PREUN" "preun" ensure_file_exists "$POSTUN" "postun" # End of the command-line processing stage RPM_BUILD_DIR=/tmp/$NAME-$VERSION-build DIRNAME=$NAME-$VERSION TARBALL=$NAME-$VERSION.tar.gz echo "Building $NAME-$VERSION-$RELEASE.$ARCH.rpm" rm -rf $RPM_BUILD_DIR install --verbose -d $RPM_BUILD_DIR/$DIRNAME # Prepare the tar file i=0 while [ $i -lt ${#PARAMS[*]} ]; do echo ${PARAMS[$i]} | ( IFS== read dstmod src echo ${dstmod} | (IFS=: read dst mod echo "${src} -> ${RPM_BUILD_DIR}/${DIRNAME}/${dst}" mkdir --parents --verbose ${RPM_BUILD_DIR}/${DIRNAME}/`dirname "${dst}"`; cp ${src} ${RPM_BUILD_DIR}/${DIRNAME}/${dst} ) ) i=$[$i+1] done # Build the install section installsect=`i=0 while [ $i -lt ${#PARAMS[*]} ]; do echo ${PARAMS[$i]} | ( IFS== read dstmod src echo ${dstmod} | (IFS=: read dst mod echo "install --verbose -d \\\$RPM_BUILD_ROOT\`dirname ${dst}\`" echo "install --verbose .${dst} \\\$RPM_BUILD_ROOT${dst}") ) i=$[$i+1] done | sort | uniq | while read line; do echo -n "$line\n"; done` # Build the file section filesect=`i=0 while [ $i -lt ${#PARAMS[*]} ]; do echo ${PARAMS[$i]} | ( IFS== read dstmod src echo ${dstmod} | (IFS=: read dst mod # Now split mod into the actual mode, the user and the group # Default to 0644,-,- # NOTE there's already a %defattr(-,root,root), so if there's no u # or g defined, set it to '-' by default echo $mod | (IFS=, read m u g echo "%attr(${m:-0644},${u:--},${g:--}) ${dst}" ) ) ) i=$[$i+1] done | sort | uniq | while read line; do echo -n "$line\n"; done` cat > $RPM_BUILD_DIR/$DIRNAME/$NAME.spec << EOF Name: $NAME $(if [ -n "$EPOCH" ]; then echo "Epoch: $EPOCH"; fi) Version: $VERSION Release: $RELEASE Group: $GROUP License: GPL BuildArch: $ARCH Source: %{name}-%{version}.tar.gz BuildRoot: /var/tmp/%{name}-%{version}-root Summary: $SUMMARY Packager: $PACKAGER Vendor: $VENDOR `generate_depend_section Requires "${REQUIRES[@]}"` `generate_depend_section Provides "${PROVIDES[@]}"` `generate_depend_section Conflicts "${CONFLICTS[@]}"` `generate_depend_section Obsoletes "${OBSOLETES[@]}"` `generate_depend_section BuildRequires "${BUILDREQ[@]}"` %description `echo -e $DESCRIPTION` %prep %setup %build %install rm -rf \$RPM_BUILD_ROOT install -d \$RPM_BUILD_ROOT `echo -e $installsect` %clean rm -rf \$RPM_BUILD_ROOT `generate_section "$POST" post` `generate_section "$PRE" pre` `generate_section "$POSTUN" postun` `generate_section "$PREUN" preun` %files %defattr(-,root,root) `echo -e $filesect` EOF # Build the tarball (cd $RPM_BUILD_DIR; tar cf - $DIRNAME | gzip -c > $RPM_BUILD_DIR/$TARBALL) rm -rf $RPM_BUILD_DIR/$DIRNAME # Build the rpm from that tarball RPMOPTS="--define \"_topdir $RPM_BUILD_DIR\"\ --define '_builddir %{_topdir}'\ --define '_sourcedir %{_topdir}'\ --define '_specdir %{_topdir}'\ --define '_rpmdir %{_topdir}'\ --define '_srcrpmdir %{_topdir}'\ --define '_source_filedigest_algorithm md5'\ --define '_binary_filedigest_algorithm md5'\ --define '_source_payload nil'\ --define '_binary_payload nil'\ " eval "rpmbuild -ta $RPMOPTS --clean $RPM_BUILD_DIR/$TARBALL" || exit 1 mv $RPM_BUILD_DIR/$ARCH/$NAME-$VERSION-$RELEASE.$ARCH.rpm . mv $RPM_BUILD_DIR/$NAME-$VERSION-$RELEASE.src.rpm . rm -rf $RPM_BUILD_DIR 07070100000007000081ED000003E80000006400000001662798DF00001786000000000000000000000000000000000000002D00000000spacewalk-certs-tools/gen-test-cert-chain.sh#!/bin/bash #################################################################### ### Use for testing only #################################################################### DIR=demoCA PASSWORD="secret" # rsa, or any of "openssl ecparam -list_curves" #PKEYALGO="secp384r1" PKEYALGO="rsa" ROOTCA="RootCA" ORGCA="OrgCa" TEAMCA="TeamCA" SRVCRT="uyuni-server-cert" SRVALTNAME="DNS:*.example.com" export country="DE" export state="STATE" export city="CITY" export org="ORG" export orgunit="ORGUNIT" mkdir -p $DIR/requests mkdir -p $DIR/private chmod 0700 $DIR/private mkdir -p $DIR/certs mkdir -p $DIR/newcerts touch $DIR/index.txt cat > $DIR/openssl.cnf <<OPENSSLCNF # ca-openssl.cnf [ ca ] default_ca = CA_default [ CA_default ] default_bits = 2048 x509_extensions = ca_x509_extensions dir = ./$DIR database = \$dir/index.txt serial = \$dir/serial new_certs_dir = \$dir/newcerts default_md = sha384 default_days = 365 # how closely we follow policy policy = policy_optional copy_extensions = copy [ policy_optional ] countryName = optional stateOrProvinceName = optional organizationName = optional organizationalUnitName = optional commonName = optional emailAddress = optional #--------------------------------------------------------------------------- [ req ] default_bits = 2048 distinguished_name = req_distinguished_name prompt = no [ req_distinguished_name ] C = \${ENV::country} ST = \${ENV::state} L = \${ENV::city} O = \${ENV::org} OU = \${ENV::orgunit} CN = \${ENV::commonname} #emailAddress = "" [ req_ca_x509_extensions ] basicConstraints = CA:true keyUsage = digitalSignature, keyEncipherment, keyCertSign extendedKeyUsage = serverAuth, clientAuth # PKIX recommendations harmless if included in all certificates. nsComment = "SSL Generated Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer:always [ req_server_x509_extensions ] basicConstraints = CA:false keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = serverAuth, clientAuth nsCertType = server # PKIX recommendations harmless if included in all certificates. nsComment = "SSL Generated Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer:always subjectAltName = \${ENV::subaltname} #=========================================================================== OPENSSLCNF export commonname=$ROOTCA export subaltname="" if [ $PKEYALGO = "rsa" ]; then openssl genrsa -out $DIR/$ROOTCA.key -passout pass:$PASSWORD -aes256 2048 else openssl ecparam -genkey -name $PKEYALGO | openssl ec -aes256 -passout pass:$PASSWORD -out $DIR/$ROOTCA.key fi openssl req -config $DIR/openssl.cnf -extensions req_ca_x509_extensions -new -x509 -key $DIR/$ROOTCA.key -out $DIR/$ROOTCA.crt -days 1024 -passin pass:$PASSWORD #----------------------------------------------------------------- export commonname=$ORGCA if [ $PKEYALGO = "rsa" ]; then openssl genrsa -out $DIR/private/$commonname.key -passout pass:$PASSWORD -aes256 2048 else openssl ecparam -genkey -name $PKEYALGO | openssl ec -aes256 -passout pass:$PASSWORD -out $DIR/private/$commonname.key fi openssl req -config $DIR/openssl.cnf -extensions req_ca_x509_extensions -new -key $DIR/private/$commonname.key -out $DIR/requests/$commonname.csr -passin pass:$PASSWORD openssl ca -config $DIR/openssl.cnf -create_serial -extensions req_ca_x509_extensions -in $DIR/requests/$commonname.csr -keyfile $DIR/$ROOTCA.key \ -cert $DIR/$ROOTCA.crt -passin pass:$PASSWORD -out $DIR/certs/$commonname.crt -days 500 -batch #----------------------------------------------------------------- export commonname=$TEAMCA if [ $PKEYALGO = "rsa" ]; then openssl genrsa -out $DIR/private/$commonname.key -passout pass:$PASSWORD -aes256 2048 else openssl ecparam -genkey -name $PKEYALGO | openssl ec -aes256 -passout pass:$PASSWORD -out $DIR/private/$commonname.key fi openssl req -config $DIR/openssl.cnf -extensions req_ca_x509_extensions -new -key $DIR/private/$commonname.key -out $DIR/requests/$commonname.csr -passin pass:$PASSWORD openssl ca -config $DIR/openssl.cnf -create_serial -extensions req_ca_x509_extensions -in $DIR/requests/$commonname.csr -keyfile $DIR/private/$ORGCA.key \ -cert $DIR/certs/$ORGCA.crt -passin pass:$PASSWORD -out $DIR/certs/$commonname.crt -days 400 -batch #----------------------------------------------------------------- export commonname=$SRVCRT export subaltname=$SRVALTNAME if [ $PKEYALGO = "rsa" ]; then openssl genrsa -out $DIR/private/$commonname.key -passout pass:$PASSWORD -aes256 2048 else openssl ecparam -genkey -name $PKEYALGO | openssl ec -aes256 -passout pass:$PASSWORD -out $DIR/private/$commonname.key fi openssl req -config $DIR/openssl.cnf -extensions req_server_x509_extensions -new -key $DIR/private/$commonname.key -out $DIR/requests/$commonname.csr -passin pass:$PASSWORD openssl ca -config $DIR/openssl.cnf -create_serial -extensions req_server_x509_extensions -in $DIR/requests/$commonname.csr -keyfile $DIR/private/$TEAMCA.key \ -cert $DIR/certs/$TEAMCA.crt -passin pass:$PASSWORD -out $DIR/certs/$commonname.crt -days 365 -batch mkdir -p $DIR/package openssl x509 -text -in $DIR/$ROOTCA.crt > $DIR/package/root-ca.crt cat $DIR/certs/$ORGCA.crt $DIR/certs/$TEAMCA.crt > $DIR/package/intermediate-ca.crt cp $DIR/certs/$SRVCRT.crt $DIR/package/server.crt if [ $PKEYALGO = "rsa" ]; then openssl rsa -passin pass:$PASSWORD -in $DIR/private/$SRVCRT.key -out $DIR/package/server.key else openssl ec -passin pass:$PASSWORD -in $DIR/private/$SRVCRT.key -out $DIR/package/server.key fi echo "Test Certificates in $DIR/package/" 07070100000008000081A4000003E80000006400000001662798DF00001B46000000000000000000000000000000000000002900000000spacewalk-certs-tools/mgr-bootstrap.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [ <!ENTITY SATANDORPROX "SUSE Manager Server and/or Proxy"> <!ENTITY EXECUTABLE "mgr-bootstrap"> ]> <refentry> <RefMeta> <RefEntryTitle>&EXECUTABLE;</RefEntryTitle><manvolnum>1</manvolnum> <RefMiscInfo>Version 4.1</RefMiscInfo> </RefMeta> <RefNameDiv> <RefName><command>&EXECUTABLE;</command></RefName> <RefPurpose> generate bootstrap scripts to aid in the deployment / configuration of SUSE Manager client systems.</RefPurpose> </RefNameDiv> <RefSynopsisDiv> <Synopsis> <cmdsynopsis> <command>&EXECUTABLE; [ options <replaceable>...</replaceable> ] [ -h|--help ]</command> </cmdsynopsis> </Synopsis> </RefSynopsisDiv> <RefSect1><Title>Description</Title> <para> <emphasis>&EXECUTABLE;</emphasis> generates bootstrap scripts to be used by SUSE Manager client systems for the purpose of simplifying their initial configuration and bootstrapping them to a &SATANDORPROX;. This is a script generator. You then use those scripts to configure & register SUSE Manager clients. </para> <para> Once the bootstrap script is generated (by default /srv/www/htdocs/pub/bootstrap/bootstrap.sh). The admin must edit it by hand to verify it is correct, and to enable it. It is then used to configure and register client systems as such: </para> <para> <command>cd /srv/www/htdocs/pub/bootstrap/</command> </para> <para> <command>cat bootstrap-<edited_name>.sh | ssh root@<client-hostname> /bin/bash</command> </para> <para> ...or... </para> <para> <command>wget -qO- https://<hostname>/pub/bootstrap/bootstrap-<edited_name>.sh | /bin/bash</command> </para> <para> ...or... </para> <para> <command>curl -Sks https://<hostname>/pub/bootstrap/bootstrap-<edited_name>.sh | /bin/bash</command> </para> </RefSect1> <RefSect1><Title>Options</Title> <variablelist> <varlistentry> <term>--activation-keys=<replaceable>ACTIVATION_KEYS</replaceable></term> <listitem> <para>activation key(s) as defined in the SUSE Manager web UI - format is XKEY,YKEY,...</para> </listitem> </varlistentry> <varlistentry> <term>--overrides=<replaceable>OVERRIDES_FILE</replaceable></term> <listitem> <para>configuration overrides filename (by default: client-config-overrides.txt).</para> </listitem> </varlistentry> <varlistentry> <term>--script=<replaceable>GENERATED_SCRIPT</replaceable></term> <listitem> <para>bootstrap script filename (by default: boostrap.sh).</para> </listitem> </varlistentry> <varlistentry> <term>--hostname=<replaceable>FQDN</replaceable></term> <listitem> <para>hostname (FQDN) to which clients connect (default: this server's hostname)</para> </listitem> </varlistentry> <varlistentry> <term>--ssl-cert=<replaceable>SSL_CERT_PATH</replaceable></term> <listitem> <para>path to corporate CA public SSL certificate - an RPM or a raw certificate. It will be copied to --pub-tree. A value of "" will force a search of --pub-tree.</para> </listitem> </varlistentry> <varlistentry> <term>--gpg-key=<replaceable>GPG_KEY_PATH</replaceable></term> <listitem> <para>path to the corporate public GPG key, if used. It will be copied to --pub-tree. (default: "").</para> </listitem> </varlistentry> <varlistentry> <term>--http-proxy=<replaceable>HOSTNAME:PORT</replaceable></term> <listitem> <para>HTTP proxy setting for the clients - hostname:port. --http-proxy="" disables. (default: "")</para> </listitem> </varlistentry> <varlistentry> <term>--http-proxy-username=<replaceable>HTTP_PROXY_USERNAME</replaceable></term> <listitem> <para>if using an authenticating HTTP proxy, specify a username. --http-proxy-username="" disables. (Default: "").</para> </listitem> </varlistentry> <varlistentry> <term>--http-proxy-password=<replaceable>HTTP_PROXY_PASSWORD</replaceable></term> <listitem> <para>if using an authenticating HTTP proxy, specify a password. (default: "")</para> </listitem> </varlistentry> <varlistentry> <term>--allow-config-actions</term> <listitem> <para>boolean; allow all configuration actions - requires installing certain rhncfg-* RPMs probably via an activation key.</para> </listitem> </varlistentry> <varlistentry> <term>--allow-remote-commands</term> <listitem> <para>boolean; allow arbitrary remote commands - requires installing certain rhncfg-* RPMs probably via an activation key.</para> </listitem> </varlistentry> <varlistentry> <term>--no-gpg</term> <listitem> <para>(not recommended) boolean; turn off GPG verification by the clients.</para> </listitem> </varlistentry> <varlistentry> <term>--no-up2date</term> <listitem> <para>(not recommended) boolean; will not run the up2date section (full update usually) once bootstrapped.</para> </listitem> </varlistentry> <varlistentry> <term>--pub-tree</term> <listitem> <para>(change not recommended) public directory tree where the CA SSL cert/cert-RPM will land as well as the bootstrap directory and scripts.</para> </listitem> </varlistentry> <varlistentry> <term>--force</term> <listitem> <para>(not recommended) boolean; force bad decisions.</para> </listitem> </varlistentry> <varlistentry> <term>-v | --verbose</term> <listitem> <para>be verbose (accumulative: -vvv means "be *really* verbose").</para> </listitem> </varlistentry> <varlistentry> <term>-h | --help</term> <listitem> <para>Display the help screen with a list of options.</para> </listitem> </varlistentry> </variablelist> </RefSect1> <RefSect1><Title>Files</Title> <simplelist> <member>/usr/bin/mgr-bootstrap</member> <member>/usr/bin/client-config-overrides.txt</member> </simplelist> </RefSect1> <RefSect1><Title>Examples</Title> <simplelist> <member><command>&EXECUTABLE; --activation-key XXX --http-proxy="" --allow-config-actions --allow-remote-commands</command></member> <member><command>&EXECUTABLE; --activation-key XXX --gpg-key ~/taw-pub.key</command></member> </simplelist> </RefSect1> <RefSect1><Title>Authors</Title> <simplelist> <member>Todd Warner <email>taw@redhat.com</email></member> </simplelist> </RefSect1> </RefEntry> 07070100000009000081A4000003E80000006400000001662798DF000012AD000000000000000000000000000000000000003A00000000spacewalk-certs-tools/mgr-package-rpm-certificate-osimage#!/usr/bin/python # # Author: Michele Bologna <michele.bologna@suse.com> # ## language imports from __future__ import print_function import os import sys import glob import pwd import time import shutil import argparse from certs.sslToolCli import CertExpTooShortException, \ CertExpTooLongException, InvalidCountryCodeException from certs.sslToolLib import RhnSslToolException, chdir, gendir from uyuni.common.fileutils import cleanupAbsPath, rhn_popen from certs.rhn_ssl_tool import legacyTreeFixup, _disableRpmMacros, _reenableRpmMacros, \ GenCaCertRpmException from certs.sslToolConfig import BUILD_DIR, CERT_PATH, CA_CERT_RPM_SUMMARY, \ CA_CRT_NAME, CA_CRT_RPM_NAME from rhn.stringutils import bstr CA_CERT_FULL_PATH_DEFAULT = "/etc/pki/trust/anchors/LOCAL-RHN-ORG-TRUSTED-SSL-CERT" def processCommandline(): parser = argparse.ArgumentParser(description="""Generates RPM certificate for OS image building""") parser.add_argument('--ca-cert-full-path', help='CA certificate filename (default: %s)' % CA_CERT_FULL_PATH_DEFAULT, default=CA_CERT_FULL_PATH_DEFAULT) parser.add_argument('--target-os', help='Target OS', default='') args = parser.parse_args() if not os.path.isfile(args.ca_cert_full_path): print("""WARNING: File '%s' cannot be opened or does not represent CA certificate.""" % args.ca_cert_full_path) print("""Please rerun '%s' with the option --ca-cert-full-path and the full path of your CA certificate to generate the RPM package needed to build OS images""" % sys.argv[0]) print("See manpage for '%s' for additional help" % sys.argv[0]) sys.exit(0) return args def genCaRpm(options): OSIMAGE_RPM_CERTIFICATE_PATH = "/srv/susemanager/salt/images" if options.target_os == 'SLE11': CA_CERT_RPM_NAME_OSIMAGE = CA_CRT_RPM_NAME + "-osimage-sle11" OSIMAGE_RPM_REQUIRES = ["openssl-certs", "coreutils"] else: CA_CERT_RPM_NAME_OSIMAGE = CA_CRT_RPM_NAME + "-osimage" OSIMAGE_RPM_REQUIRES = ["ca-certificates", "shadow"] ca_cert_name = os.path.basename(options.ca_cert_full_path) ca_cert = options.ca_cert_full_path ca_cert_rpm_osimage = os.path.join(OSIMAGE_RPM_CERTIFICATE_PATH, CA_CERT_RPM_NAME_OSIMAGE) ver, rel = '1.0', '1' update_trust_script = os.path.join(CERT_PATH, 'update-ca-cert-trust.sh') args = (os.path.join(CERT_PATH, 'gen-rpm.sh') + " " "--name %s --version %s --release %s --packager %s --vendor %s " "%s " "--group 'RHN/Security' --summary %s --description %s " "--post %s --postun %s " "/usr/share/rhn/%s=%s" % (repr(CA_CERT_RPM_NAME_OSIMAGE), ver, rel, None, None, " ".join("--requires " + r for r in OSIMAGE_RPM_REQUIRES), repr(CA_CERT_RPM_SUMMARY), repr(CA_CERT_RPM_SUMMARY), repr(update_trust_script), repr(update_trust_script), repr(CA_CRT_NAME), repr(cleanupAbsPath(ca_cert)))) clientRpmName = '%s-%s-%s' % (ca_cert_rpm_osimage, ver, rel) print("CA Cert for OS Images: Packaging %s into %s.noarch.rpm" % (ca_cert, clientRpmName)) _disableRpmMacros() gendir(OSIMAGE_RPM_CERTIFICATE_PATH) shutil.chown(OSIMAGE_RPM_CERTIFICATE_PATH, "salt", "salt") os.chmod(OSIMAGE_RPM_CERTIFICATE_PATH, int("0755", 8)) cwd = chdir(OSIMAGE_RPM_CERTIFICATE_PATH) try: ret, out_stream, err_stream = rhn_popen(args) except Exception: chdir(cwd) _reenableRpmMacros() raise chdir(cwd) _reenableRpmMacros() out = out_stream.read(); out_stream.close() err = err_stream.read(); err_stream.close() if ret or not os.path.exists("%s.noarch.rpm" % clientRpmName): raise GenCaCertRpmException("CA public SSL certificate RPM generation " "failed:\n%s\n%s" % (out, err)) os.chmod('%s.noarch.rpm' % clientRpmName, int('0644',8)) shutil.chown('%s.noarch.rpm' % clientRpmName, "salt", "salt") return '%s.noarch.rpm' % clientRpmName def _main(): """ main routine """ options = processCommandline() genCaRpm(options) def main(): def writeError(e): sys.stderr.write(bstr('\nERROR: %s\n' % e)) ret = 0 try: ret = _main() or 0 # CA key set errors except GenCaCertRpmException as e: writeError(e) ret = 12 except RhnSslToolException as e: writeError(e) ret = 100 return ret #------------------------------------------------------------------------------- if __name__ == "__main__": main() #=============================================================================== 0707010000000A000081A4000003E80000006400000001662798DF00000C09000000000000000000000000000000000000003F00000000spacewalk-certs-tools/mgr-package-rpm-certificate-osimage.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [ <!ENTITY mgr-package-rpm-certificate-osimage "SUSE Manager OS image SSL Maintenance Tool"> ]> <refentry> <RefMeta> <RefEntryTitle>mgr-package-rpm-certificate-osimage</RefEntryTitle><manvolnum>1</manvolnum> <RefMiscInfo>Version 1.0.0</RefMiscInfo> </RefMeta> <RefNameDiv> <RefName><command>mgr-package-rpm-certificate-osimage</command></RefName> <RefPurpose> Generate RPM package containing the CA certificate for the server </RefPurpose> </RefNameDiv> <RefSynopsisDiv> <Synopsis> <cmdsynopsis> <command>mgr-package-rpm-certificate-osimage</command> <arg>options <replaceable>...</replaceable></arg> <command>--help</command> </cmdsynopsis> <cmdsynopsis> <command>mgr-ssl-tool --ca-cert-full-path<replaceable>CA_CERT_FULL_PATH</replaceable></command> <arg>options <replaceable>...</replaceable></arg> </cmdsynopsis> </Synopsis> </RefSynopsisDiv> <RefSect1><Title>Help</Title> <simplelist> <member><command>mgr-package-rpm-certificate-osimage --help</command></member> <member><command>mgr-package-rpm-certificate-osimage --ca-cert-full-path</command></member> </simplelist> </RefSect1> <RefSect1><Title>Description</Title> <para>The <command>mgr-package-rpm-certificate-osimage</command>) is used to generate the RPM package containing the SUSE Manager's CA certificate.</para> <para>This RPM is used in OS image build process to trust SUSE Manager when accessing its channels. The RPM is the same as the one created by <emphasis>mgr-ssl-tool</emphasis> with the only difference that the RPM generated by <command>mgr-package-rpm-certificate-osimage</command> is depending on `ca-certificates` to make Kiwi into installing the whole trust chain.</para> <para>SUSE Manager Servers use <emphasis>/root/ssl-build</emphasis> as the default build directory.</para> <para>Normally, on new installations, <command>mgr-package-rpm-certificate-osimage </command> is automatically called upon installation. For upgrades, system administrator should call <command>mgr-package-rpm-certificate-osimage </command>. If the command is called without parameters, it tries to package the certificate found in the default directory <emphasis>/root/ssl-build</emphasis>. If the generated CA certificate is in another directory, the full path of the certificate must be specified by using <emphasis>--ca-cert-full-path CA_CERT_FULL_PATH</emphasis></para> <para> The generated RPM will be placed in: <emphasis>/usr/share/susemanager/salt/images</emphasis>. If a RPM is already present in the target location, it will be overwritten. </para> </RefSect1> <RefSect1><Title>See Also</Title> <simplelist> <member>mgr-ssl-tool</member> <member>openssl(1)</member> <member>rpm(8)</member> </simplelist> </RefSect1> <RefSect1><Title>Author</Title> <simplelist> <member>Michele Bologna<email>michele.bologna@suse.com</email></member> </simplelist> </RefSect1> </RefEntry> 0707010000000B000081ED000003E80000006400000001662798DF00000513000000000000000000000000000000000000002900000000spacewalk-certs-tools/mgr-ssl-cert-setup#!/usr/bin/python3 # # Copyright (c) 2021, SUSE LLC # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # SSL Certificate Setup Tool # # ## language imports import sys import os try: import certs.mgr_ssl_cert_setup except KeyboardInterrupt: sys.stderr.write("\nUser interrupted process.\n") sys.exit(0) except ImportError as e: sys.stderr.write("Unable to load module certs.mgr_ssl_cert_setup\n") sys.stderr.write(str(e) + "\n") sys.exit(1) #------------------------------------------------------------------------------- if __name__ == '__main__': try: sys.exit(certs.mgr_ssl_cert_setup.main() or 0) except KeyboardInterrupt: sys.stderr.write("\nUser interrupted process.\n") sys.exit(0) except SystemExit: raise except: sys.stderr.write("\nERROR: unhandled exception occurred:\n") raise #=============================================================================== 0707010000000C000081A4000003E80000006400000001662798DF00007735000000000000000000000000000000000000002800000000spacewalk-certs-tools/mgr-ssl-tool.sgml<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [ <!ENTITY RHNSSLTOOL "SUSE Manager SSL Maintenance Tool"> ]> <refentry> <RefMeta> <RefEntryTitle>mgr-ssl-tool</RefEntryTitle><manvolnum>1</manvolnum> <RefMiscInfo>Version 1.0.1</RefMiscInfo> </RefMeta> <RefNameDiv> <RefName><command>mgr-ssl-tool</command></RefName> <RefPurpose> Generate and maintain SSL keys, certificates and deployment RPMs. </RefPurpose> </RefNameDiv> <RefSynopsisDiv> <Synopsis> <cmdsynopsis> <command>mgr-ssl-tool</command> <arg>options <replaceable>...</replaceable></arg> <command>--help</command> </cmdsynopsis> <cmdsynopsis> <command>mgr-ssl-tool --gen-ca -d<replaceable>BUILD_DIR</replaceable> -p<replaceable>CA_PASSWORD</replaceable></command> <arg>options <replaceable>...</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <command>mgr-ssl-tool --gen-server -d<replaceable>BUILD_DIR</replaceable> -p<replaceable>CA_PASSWORD</replaceable></command> <arg>options <replaceable>...</replaceable></arg> </cmdsynopsis> </Synopsis> </RefSynopsisDiv> <RefSect1><Title>Help</Title> <simplelist> <member><command>mgr-ssl-tool --help</command></member> <member><command>mgr-ssl-tool --gen-ca --help</command></member> <member><command>mgr-ssl-tool --gen-server --help</command></member> <member>- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -</member> <member>(advanced) <command>mgr-ssl-tool --gen-ca --key-only --help</command></member> <member>(advanced) <command>mgr-ssl-tool --gen-ca --cert-only --help</command></member> <member>(advanced) <command>mgr-ssl-tool --gen-ca --rpm-only --help</command></member> <member>- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -</member> <member>(advanced) <command>mgr-ssl-tool --gen-server --key-only --help</command></member> <member>(advanced) <command>mgr-ssl-tool --gen-server --cert-req-only --help</command></member> <member>(advanced) <command>mgr-ssl-tool --gen-server --cert-only --help</command></member> <member>(advanced) <command>mgr-ssl-tool --gen-server --rpm-only --help</command></member> </simplelist> </RefSect1> <RefSect1><Title>Description</Title> <para>The &RHNSSLTOOL; (<command>mgr-ssl-tool</command>) is used to generate and maintain SUSE Manager SSL keys and certificates. It also will generate RPMs for use in deploying these keys and certificates. The tool is geared for use in an SUSE Manager context, but can be useful outside of SUSE Manager.</para> <para>Working with <command>openssl</command> directly can be tedious and trying. This tool aims to make the process relatively simple. We limit the scope of using <command>openssl</command> to how we use it in SUSE Manager: securing web applications.</para> <para>The SUSE Manager context of this document is in support of SUSE Manager server, but all holds true for SUSE Manager Proxy as well, so keep that in mind. SUSE Manager Servers and Proxies use <emphasis>/root/ssl-build</emphasis> as the default build directory.</para> <para>The basic process of SSL key/certificate/RPM generation using this tool: (<emphasis>step 1</emphasis>) generate a CA SSL key pair(set) and public RPM, (<emphasis>step 2</emphasis>) create web server SSL key pair(set) and RPM (and tar archive).</para> <para><emphasis>Build directory structure</emphasis>: <command>--dir <replaceable>BUILD_DIR</replaceable></command> is used with nearly all commandline options. <emphasis><replaceable>BUILD_DIR</replaceable></emphasis> marks the top of the build tree; all CA files and RPMs land there. Server SSL key pairs(sets) are FQDN specific and so we build them in <emphasis><replaceable>BUILD_DIR/MACHINE_NAME</replaceable></emphasis>.</para> <para><simplelist> <member><msgtext> <variablelist><varlistentry> <term>STEP 1: Generate a CA key pair(set) and public RPM:</term> <listitem> <para></para> <para><command>mgr-ssl-tool --gen-ca --dir <replaceable>BUILD_DIR</replaceable> [ options <replaceable>...</replaceable> ]</command></para> <para>This step should ideally never need to be repeated unless the CA password is lost or forgotten (DON'T DO THAT!). The default validity window for the CA is from now until 2038. The CA public certificate is what get's distributed to clients of the web-app (SUSE Manager Server/Proxy).</para> <para>In the SUSE Manager Server/Proxy context, the organization acts as their own Certificate Authority, but these steps can be skipped if intending to use of an outside authority.</para> <para>The CA private key remains private.</para> <para>The CA certificate is used by client software (zypper for example), and is generally deployed via an RPM or the raw file.</para> </listitem> </varlistentry></variablelist> </msgtext></member> <member><msgtext> <variablelist><varlistentry> <term>STEP 2: Generate a web server SSL key pair(set), RPM and tar archive: </term> <listitem> <para></para> <para><command>mgr-ssl-tool --gen-server --dir <replaceable>BUILD_DIR</replaceable> [ options <replaceable>...</replaceable> ]</command></para> <para>This step is done more frequently (generally), especially if more than one SUSE Manager server is being deployed (--set-hostname is different for each server). The default validity window for the CA is from now until 2038. All clients using the CA SSL public certificate that signed the new web server SSL certificate will work as expected with all web server key pairs(set) generated.</para> <para>The web server SSL key and certificate are used solely by the web application server (apache on an SUSE Manager Proxy or Server for example).</para> </listitem> </varlistentry></variablelist> </msgtext></member> <member><msgtext> <variablelist><varlistentry> <term>A note about generated RPMs:</term> <listitem> <para>The <command>--gen-ca</command> process generates an RPM that contains the public CA certificate. It needs to be deployed to any clients making SSL connections to an SUSE Manager server (SUSE Manager Server or Proxy). This is generally done by making the RPM available in the <emphasis>/srv/www/htdocs/pub</emphasis> directory. It is also a good idea to copy the CA certificate itself in that directory: RHN-ORG-TRUSTED-CA-CERT.</para> <para>The <command>--gen-server</command> process generates an RPM that contains the <emphasis>server.key</emphasis> and <emphasis>server.crt</emphasis> files needed to secure your SUSE Manager Server or SUSE Manager Proxy. It needs to be installed on the appropriate server. That server then needs to have it's <emphasis>httpd</emphasis> processes restarted <command>/sbin/service httpd restart</command>.</para> </listitem> </varlistentry></variablelist> <variablelist><varlistentry> <term>IMPORTANT:</term> <listitem> <para>Time and date need to be correctly set on systems establishing SSL connections. It is highly recommended that all client and server systems have the <emphasis>ntpd</emphasis> service installed, configured and running.</para> </listitem> </varlistentry></variablelist> </msgtext></member> <member><msgtext> <variablelist><varlistentry> <term>Advanced options (rarely used discrete steps):</term> <listitem> <para></para> <para>generate a CA SSL private key: <command>--gen-ca --key-only <replaceable>...</replaceable></command></para> <para>generate a CA SSL public certificate: <command>--gen-ca --cert-only <replaceable>...</replaceable></command></para> <para>generate a CA SSL public RPM: <command>--gen-ca --rpm-only <replaceable>...</replaceable></command></para> <para>generate a CA SSL public RPM using a custom CA certificate from the given file: <command>--gen-ca --rpm-only --from-ca-cert=<replaceable>FILE</replaceable></command></para> <para>generate a web server's SSL private key: <command>--gen-server --key-only <replaceable>...</replaceable></command></para> <para>generate a web server's SSL certificate request: <command>--gen-server --cert-req-only <replaceable>...</replaceable></command></para> <para>generate/sign a web server's SSL certificate: <command>--gen-server --cert-only <replaceable>...</replaceable></command></para> <para>generate a web server's private RPM (and tar archive used for SUSE Manager Proxy installations): <command>--gen-server --rpm-only <replaceable>...</replaceable></command></para> <para>generate a web server's private RPM using a custom SSL key and certificate: <command>--gen-server --rpm-only --from-server-key=<replaceable>FILE</replaceable> --from-server-cert=<replaceable>FILE</replaceable></command></para> </listitem> </varlistentry></variablelist> </msgtext></member> <member><msgtext> <variablelist><varlistentry> <term>Using a 3rd party CA (rarely done in the SUSE Manager context):</term> <listitem> <para><emphasis>DEPRECATED:</emphasis> Use <command>--from-ca-cert</command>, <command>--from-server-key</command> and <command>--from-server-cert</command> parameters instead as described in Advanced options section. </para> <listitem> <para></para> <para><emphasis>CA public certificate:</emphasis> In the "3rd party CA" case, simply copy the certificate authorities public certificate to the SSL build directory; renaming it to <emphasis>RHN-ORG-TRUSTED-SSL-CERT</emphasis>; and then run <command>--gen-ca --dir BUILD_DIR --rpm-only</command> to package that certificate in an expected manner ready for client deployment. See further instructions in <emphasis>step 2</emphasis>.</para> <para><emphasis>Web server's SSL key pair(set):</emphasis> Usually, one creates the web server's SSL private key, certificate-request and certificate in one step. If using a 3rd party CA though, create a web server's SSL private key and certificate-request via <command>--gen-server --key-only --dir BUILD_DIR</command> and <command>--gen-server --cert-req-only --dir BUILD_DIR</command>. Have the 3rd party sign server.csr which will generate a server.crt file. Copy that server.crt file into the <emphasis>BUILD_DIR/MACHINE_NAME</emphasis> directory (where the server.key file was generated). And then create your deployable RPM with <command>--gen-server --rpm-only --dir BUILD_DIR</command>. </para> </listitem> </varlistentry></variablelist> </msgtext></member> </simplelist></para> <para>NOTE: each step (<command>--gen-*</command> or <command>--gen-* --*-only</command>) has its own <command>--help</command> information.</para> </RefSect1> <RefSect1><Title>All options</Title> <variablelist> <varlistentry> <term>-h | --help</term> <listitem> <para>Display the help screen with a list of base options (--gen-*).</para> </listitem> </varlistentry> <varlistentry> <term>--gen-ca</term> <listitem> <para>Generate a Certificate Authority (CA) key pair and public RPM:</para> <variablelist> <varlistentry> <term>-f | --force</term> <listitem> <para>forcibly create a new CA private key and/or public certificate.</para> </listitem> </varlistentry> <varlistentry> <term>-p<replaceable>PASSWORD</replaceable> | --password=<replaceable>PASSWORD</replaceable></term> <listitem> <para>CA password. Will prompt if option is missing.</para> </listitem> </varlistentry> <varlistentry> <term>-d <replaceable>BUILD_DIR</replaceable> | --dir=<replaceable>BUILD_DIR</replaceable></term> <listitem> <para>build directory (default: ./ssl-build).</para> </listitem> </varlistentry> <varlistentry> <term>--ca-key=<replaceable>FILENAME</replaceable></term> <listitem> <para>CA private key filename(default is dynamically set).</para> </listitem> </varlistentry> <varlistentry> <term>--ca-cert=<replaceable>FILENAME</replaceable></term> <listitem> <para>CA public certificate filename (default is dynamically set).</para> </listitem> </varlistentry> <varlistentry> <term>--cert-expiration=<replaceable>CA_CERT_EXP</replaceable></term> <listitem> <para>expiration of public CA certificate (default is # days until 1 day prior to epoch rollover (or 01-19-2038)).</para> </listitem> </varlistentry> <varlistentry> <term>--set-country=<replaceable>COUNTRY_CODE</replaceable></term> <listitem> <para>two letter country code (default: US). Use capital letters only!</para> </listitem> </varlistentry> <varlistentry> <term>--set-state=<replaceable>STATE_OR_PROVINCE</replaceable></term> <listitem> <para>state or province, such as "Frankonia" (default: "")</para> </listitem> </varlistentry> <varlistentry> <term>--set-city=<replaceable>CITY_OR_LOCALITY</replaceable></term> <listitem> <para>city or locality, such as "Nuremberg" (default: "").</para> </listitem> </varlistentry> <varlistentry> <term>--set-org=<replaceable>ORGANIZATION</replaceable></term> <listitem> <para>organization or company name (default: "Example Corp. Inc").</para> </listitem> </varlistentry> <varlistentry> <term>--set-org-unit=<replaceable>ORGANIZATIONAL_UNIT</replaceable></term> <listitem> <para>organizational unit (default: unit).</para> </listitem> </varlistentry> <varlistentry> <term>--set-common-name=<replaceable>HOSTNAME</replaceable></term> <listitem> <para>not generally set for the CA certificate. The common name.</para> </listitem> </varlistentry> <varlistentry> <term>--set-email=<replaceable>EMAIL</replaceable></term> <listitem> <para>email address. Not generally set for the CA certificate.</para> </listitem> </varlistentry> <varlistentry> <term>-v | --verbose</term> <listitem> <para>be verbose (accumulative: -vvv means "be *really* verbose).</para> </listitem> </varlistentry> <varlistentry> <term>--key-only</term> <listitem> <para>(rarely used) only generate a CA private key. Try <command>--gen-ca --key-only --help</command> for more information.</para> </listitem> </varlistentry> <varlistentry> <term>--cert-only</term> <listitem> <para>(rarely used) only generate a CA public certificate. Try <command>--gen-ca --cert-only --help</command> for more information.</para> </listitem> </varlistentry> <varlistentry> <term>--ca-cert-rpm</term> <listitem> <para>(rarely changed) RPM name that houses the CA SSL public certificate (the base filename, not filename-version-release.noarch.rpm).</para> </listitem> </varlistentry> <varlistentry> <term>--rpm-packager</term> <listitem> <para>(rarely used) packager of the generated RPM, such as "SUSE Manager Admin <rhn-admin@example.com>".</para> </listitem> </varlistentry> <varlistentry> <term>--rpm-vendor</term> <listitem> <para>(rarely used) vendor of the generated RPM, such as "IS/IT Example Corp.".</para> </listitem> </varlistentry> <varlistentry> <term>--rpm-only</term> <listitem> <para>(rarely used) only generate a deployable RPM. Try <command>--gen-ca --rpm-only --help</command> for more information.</para> </listitem> </varlistentry> <varlistentry> <term>--no-rpm</term> <listitem> <para>(rarely used) do everything *except* generate an RPM.</para> </listitem> </varlistentry> <varlistentry> <term>-h | --help</term> <listitem> <para>help message.</para> </listitem> </varlistentry> </variablelist> </listitem> </varlistentry> <varlistentry> <term>--gen-server</term> <listitem> <para>Generate a web server's SSL key pair(set), RPM and tar archive:</para> <variablelist> <varlistentry> <term>-p<replaceable>PASSWORD</replaceable> | --password=<replaceable>PASSWORD</replaceable></term> <listitem> <para>CA password. Will prompt if option is missing. MUST MATCH PASSWORD OF CA!!!</para> </listitem> </varlistentry> <varlistentry> <term>-d <replaceable>BUILD_DIR</replaceable> | --dir=<replaceable>BUILD_DIR</replaceable></term> <listitem> <para>build directory (default: ./ssl-build).</para> </listitem> </varlistentry> <varlistentry> <term>--server-key=<replaceable>FILENAME</replaceable></term> <listitem> <para>web server's SSL private key filename (default: server.key).</para> </listitem> </varlistentry> <varlistentry> <term>--server-cert-req=<replaceable>FILENAME</replaceable></term> <listitem> <para>web server's SSL certificate request filename (default: server.csr).</para> </listitem> </varlistentry> <varlistentry> <term>--server-cert=<replaceable>FILENAME</replaceable></term> <listitem> <para>web server's SSL certificate filename (default: server.crt).</para> </listitem> </varlistentry> <varlistentry> <term>--startdate=<replaceable>YYMMDDHHMMSSZ</replaceable></term> <listitem> <para>start date for web server's SSL certificate validity in the above format (Z is a letter; default is 1 week ago).</para> </listitem> </varlistentry> <varlistentry> <term>--cert-expiration=<replaceable>EXPIRATION</replaceable></term> <listitem> <para>expiration of the web server's SSL certificate (default is # days until 1 day prior to epoch rollover (or 01-19-2038)).</para> </listitem> </varlistentry> <varlistentry> <term>--set-country=<replaceable>COUNTRY_CODE</replaceable></term> <listitem> <para>two letter country code (default: US). Use capital letters only!</para> </listitem> </varlistentry> <varlistentry> <term>--set-state=<replaceable>STATE_OR_PROVINCE</replaceable></term> <listitem> <para>state or province (default: "Frankonia")</para> </listitem> </varlistentry> <varlistentry> <term>--set-city=<replaceable>CITY_OR_LOCALITY</replaceable></term> <listitem> <para>city or locality (default: Raleigh).</para> </listitem> </varlistentry> <varlistentry> <term>--set-org=<replaceable>ORGANIZATION</replaceable></term> <listitem> <para>organization or company name (default: "Example Corp. Inc").</para> </listitem> </varlistentry> <varlistentry> <term>--set-org-unit=<replaceable>ORGANIZATIONAL_UNIT</replaceable></term> <listitem> <para>organizational unit, such as "Marketing" (default: unit).</para> </listitem> </varlistentry> <varlistentry> <term>--set-hostname=<replaceable>HOSTNAME</replaceable></term> <listitem> <para>set the hostname (FQDN: fully qualified domain name) of the SUSE Manager Server or SUSE Manager Proxy (default: build machine's hostname).</para> </listitem> </varlistentry> <varlistentry> <term>--set-cname=<replaceable>HOSTNAME</replaceable></term> <listitem> <para>set the cname alias (FQDN: fully qualified domain name) of the SUSE Manager Server or SUSE Manager Proxy. This will generate certificate with multiple hostnames. Can be specified multiple times. </para> </listitem> </varlistentry> <varlistentry> <term>--set-email=<replaceable>EMAIL</replaceable></term> <listitem> <para>email address (default: admin@example.com)</para> </listitem> </varlistentry> <varlistentry> <term>-v | --verbose</term> <listitem> <para>be verbose (accumulative: -vvv means "be *really* verbose).</para> </listitem> </varlistentry> <varlistentry> <term>--key-only</term> <listitem> <para>(rarely used) only generate a web server's SSL private key. Try <command>--gen-server --key-only --help</command> for more information.</para> </listitem> </varlistentry> <varlistentry> <term>--cert-req-only</term> <listitem> <para>(rarely used) only generate a web server's SSL certificate request. Try <command>--gen-server --cert-req-only --help</command> for more information.</para> </listitem> </varlistentry> <varlistentry> <term>--cert-only</term> <listitem> <para>(rarely used) only generate a web server's SSL certificate. Try <command>--gen-server --cert-only --help</command> for more information.</para> </listitem> </varlistentry> <varlistentry> <term>--server-rpm</term> <listitem> <para>(rarely changed) RPM name that houses the web server's SSL key set (the base filename, not filename-version-release.noarch.rpm).</para> </listitem> </varlistentry> <varlistentry> <term>--server-tar</term> <listitem> <para>(rarely changed) name of archive (tarball) of the web server's SSL key set and CA SSL public certificate that is used solely by the hosted SUSE Manager installation routines (the base filename, not filename-version-release.tar).</para> </listitem> </varlistentry> <varlistentry> <term>--rpm-packager</term> <listitem> <para>(rarely used) packager of the generated RPM, such as "SUSE Manager Admin <rhn-admin@example.com>".</para> </listitem> </varlistentry> <varlistentry> <term>--rpm-vendor</term> <listitem> <para>(rarely used) vendor of the generated RPM, such as "IS/IT Example Corp.".</para> </listitem> </varlistentry> <varlistentry> <term>--rpm-only</term> <listitem> <para>(rarely used) only generate a deployable RPM. Try <command>--gen-server --rpm-only --help</command> for more information.</para> </listitem> </varlistentry> <varlistentry> <term>--no-rpm</term> <listitem> <para>(rarely used) do everything *except* generate an RPM.</para> </listitem> </varlistentry> <varlistentry> <term>-h | --help</term> <listitem> <para>help message.</para> </listitem> </varlistentry> </variablelist> </listitem> </varlistentry> </variablelist> </RefSect1> <RefSect1><Title>Examples</Title> <simplelist> <member><command>mgr-ssl-tool --help</command></member> <member><command>mgr-ssl-tool --gen-ca --help</command></member> <member><command>mgr-ssl-tool --gen-server --help</command></member> <member><command>mgr-ssl-tool --gen-ca -pMY_CA_PASSWORD --set-state="Frankonia" --set-city=Nuremberg --set-org="Example Inc." --set-org-unit="SSL CA Unit" --dir=/etc/sysconfig/rhn/ssl</command></member> <member><command>mgr-ssl-tool --gen-server -pMY_CA_PASSWORD --set-state="Frankonia" --set-city=Nuremberg --set-org="Example Inc." --set-org-unit="IS/IT" --email="taw@example.com" --set-hostname="rhnbox1.example.com" --set-cname="rhnbox1.localnet" --dir=/etc/sysconfig/rhn/ssl</command></member> </simplelist> </RefSect1> <RefSect1><Title>Files</Title> <simplelist> <member>BUILD_DIR/rhn-ca-openssl.cnf</member> <member>BUILD_DIR/RHN-ORG-PRIVATE-SSL-KEY</member> <member>BUILD_DIR/RHN-ORG-TRUSTED-SSL-CERT</member> <member>BUILD_DIR/serial</member> <member>BUILD_DIR/index.txt</member> <member>BUILD_DIR/latest.txt</member> <member>BUILD_DIR/rhn-org-trusted-ssl-cert-VER-REL.src.rpm</member> <member>BUILD_DIR/rhn-org-trusted-ssl-cert-VER-REL.noarch.rpm</member> <member>BUILD_DIR/MACHINE_NAME/latest.txt</member> <member>BUILD_DIR/MACHINE_NAME/rhn-server-openssl.cnf</member> <member>BUILD_DIR/MACHINE_NAME/server.key</member> <member>BUILD_DIR/MACHINE_NAME/server.csr</member> <member>BUILD_DIR/MACHINE_NAME/server.crt</member> <member>BUILD_DIR/MACHINE_NAME/rhn-org-httpd-ssl-key-pair-MACHINE_NAME-VER-REL.src.rpm</member> <member>BUILD_DIR/MACHINE_NAME/rhn-org-httpd-ssl-key-pair-MACHINE_NAME-VER-REL.noarch.rpm</member> <member>BUILD_DIR/MACHINE_NAME/rhn-org-httpd-ssl-archive-MACHINE_NAME-VER-REL.tar</member> </simplelist> </RefSect1> <RefSect1><Title>See Also</Title> <simplelist> <member>openssl(1)</member> <member>rpm(8)</member> </simplelist> </RefSect1> <RefSect1><Title>Author</Title> <simplelist> <member>Todd Warner <email>taw@redhat.com</email></member> </simplelist> </RefSect1> </RefEntry> 0707010000000D000081ED000003E80000006400000001662798DF00006065000000000000000000000000000000000000002C00000000spacewalk-certs-tools/mgr_ssl_cert_setup.py#!/usr/bin/python3 # pylint: disable=missing-module-docstring # # Copyright (c) 2021, SUSE LLC # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # ## language imports import os import sys import argparse import traceback # pylint: disable-next=unused-import import shutil # pylint: disable-next=unused-import import tempfile import time import subprocess from collections import namedtuple from datetime import datetime from spacewalk.common.rhnLog import initLOG, log_time, log_clean from uyuni.common.fileutils import getUidGid LOGFILE = "/var/log/rhn/mgr-ssl-cert-setup.log" PKI_DIR = "/etc/pki/" SRV_KEY_NAME = "spacewalk.key" APACHE_CRT_NAME = "spacewalk.crt" APACHE_CRT_FILE = os.path.join(PKI_DIR, "tls", "certs", APACHE_CRT_NAME) APACHE_KEY_FILE = os.path.join(PKI_DIR, "tls", "private", SRV_KEY_NAME) PG_KEY_FILE = os.path.join(PKI_DIR, "tls", "private", "pg-" + SRV_KEY_NAME) ROOT_CA_NAME = "RHN-ORG-TRUSTED-SSL-CERT" PKI_ROOT_CA_NAME = "LOCAL-" + ROOT_CA_NAME ROOT_CA_HTTP_DIR = "/srv/www/htdocs/pub/" if not os.path.exists(ROOT_CA_HTTP_DIR): # Red Hat ROOT_CA_HTTP_DIR = "/var/www/html/pub/" CA_TRUST_DIR = os.path.join(PKI_DIR, "trust", "anchors") if not os.path.exists(CA_TRUST_DIR): # Red Hat CA_TRUST_DIR = os.path.join(PKI_DIR, "ca-trust", "source", "anchors") SALT_CA_DIR = "/usr/share/susemanager/salt/certs/" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - class CertCheckError(Exception): pass FilesContent = namedtuple( "FilesContent", ["root_ca", "server_cert", "server_key", "intermediate_cas"] ) def log_error(msg): frame = traceback.extract_stack()[-2] log_clean( 0, # pylint: disable-next=consider-using-f-string "{0}: {1}.{2}({3}) - {4}".format(log_time(), frame[0], frame[2], frame[1], msg), ) # pylint: disable-next=consider-using-f-string sys.stderr.write("{0}\n".format(msg)) def log(msg, level=0): frame = traceback.extract_stack()[-2] log_clean( level, # pylint: disable-next=consider-using-f-string "{0}: {1}.{2}({3}) - {4}".format(log_time(), frame[0], frame[2], frame[1], msg), ) if level < 1: # pylint: disable-next=consider-using-f-string sys.stdout.write("{0}\n".format(msg)) # pylint: disable-next=invalid-name def processCommandline(): usage = "%(prog)s [options] [command]" parser = argparse.ArgumentParser(usage=usage) parser.add_argument("-r", "--root-ca-file", help="Path to the Root CA") parser.add_argument( "-i", "--intermediate-ca-file", action="append", default=[], help="Path to an intermediate CA", ) parser.add_argument( "-s", "--server-cert-file", help="Path to the Server Certificate" ) parser.add_argument( "-k", "--server-key-file", help="Path to the Server Private Key" ) parser.add_argument("--check-only", "-c", action="store_true") parser.add_argument("--verbose", "-v", action="count", default=0) options = parser.parse_args() initLOG(LOGFILE, options.verbose or 1) log(sys.argv, 1) return options # pylint: disable-next=invalid-name def checkOptions( root_ca_file, server_cert_file, server_key_file, intermediate_ca_files ): if not root_ca_file: log_error("Root CA is required") sys.exit(1) if not os.path.exists(root_ca_file): # pylint: disable-next=consider-using-f-string log_error("Root CA: file not found {}".format(root_ca_file)) sys.exit(1) if not server_cert_file: log_error("Server Certificate is required") sys.exit(1) if not os.path.exists(server_cert_file): # pylint: disable-next=consider-using-f-string log_error("Server Certificate: file not found {}".format(server_cert_file)) sys.exit(1) if not server_key_file: log_error("Server Private Key is required") sys.exit(1) if not os.path.exists(server_key_file): # pylint: disable-next=consider-using-f-string log_error("Server Private Key: file not found {}".format(server_key_file)) sys.exit(1) for ica in intermediate_ca_files: if not os.path.exists(ica): # pylint: disable-next=consider-using-f-string log_error("Intermediate CA: file not found {}".format(ica)) sys.exit(1) # pylint: disable-next=invalid-name def readAllFiles( root_ca_file, server_cert_file, server_key_file, intermediate_ca_files ): # pylint: disable-next=invalid-name allFiles = [root_ca_file, server_cert_file, server_key_file] allFiles.extend(intermediate_ca_files) contents = [] for input_file in allFiles: # pylint: disable-next=unspecified-encoding with open(input_file, "r") as f: contents.append(f.read()) return FilesContent( root_ca=contents[0], server_cert=contents[1], server_key=contents[2], intermediate_cas=contents[3:], ) # pylint: disable-next=invalid-name def prepareData(root_ca_content, server_cert_content, intermediate_ca_content): """ Create a result dict with all certificates and pre-parsed data with the subject_hash as key. """ ret = dict() # pylint: disable-next=invalid-name allCAs = [root_ca_content] allCAs.extend(intermediate_ca_content) # pylint: disable-next=invalid-name isContent = False content = [] for ca in allCAs: cert = "" for line in ca.splitlines(keepends=True): if not isContent and line.startswith("-----BEGIN"): # pylint: disable-next=invalid-name isContent = True cert = "" if isContent: cert += line if isContent and line.startswith("-----END"): content.append(cert) # pylint: disable-next=invalid-name isContent = False for cert in content: data = getCertData(cert) data["content"] = cert shash = data["subject_hash"] if shash: ret[shash] = data data = getCertData(server_cert_content) shash = data["subject_hash"] if shash: ret[shash] = data return ret # pylint: disable-next=invalid-name def isCA(cert): # pylint: disable-next=subprocess-run-check out = subprocess.run( ["openssl", "x509", "-noout", "-ext", "basicConstraints"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=cert.encode("utf-8"), ) if out.returncode: log_error( # pylint: disable-next=consider-using-f-string "Unable to parse the certificate: {}".format(out.stderr.decode("utf-8")) ) return False for line in out.stdout.decode("utf-8").splitlines(): if "CA:TRUE" in line.upper(): return True return False # pylint: disable-next=invalid-name def isValid(startdate, enddate, subject): # Not Before: Nov 12 14:36:13 2021 GMT # Not After : Sep 1 14:36:13 2024 GMT start = datetime.strptime(startdate, "%b %d %H:%M:%S %Y %Z") end = datetime.strptime(enddate, "%b %d %H:%M:%S %Y %Z") now = datetime.utcnow() if now < start: # pylint: disable-next=consider-using-f-string raise CertCheckError("Certificate '{}' not yet valid".format(subject)) if now > end: # pylint: disable-next=consider-using-f-string raise CertCheckError("Certificate '{}' is expired".format(subject)) # pylint: disable-next=invalid-name def getCertData(cert): data = dict() # pylint: disable-next=subprocess-run-check out = subprocess.run( [ "openssl", "x509", "-noout", "-subject", "-subject_hash", "-startdate", "-enddate", "-issuer", "-issuer_hash", "-modulus", "-ext", "subjectKeyIdentifier,authorityKeyIdentifier", ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=cert.encode("utf-8"), ) if out.returncode: log_error( # pylint: disable-next=consider-using-f-string "Unable to parse the certificate: {}".format(out.stderr.decode("utf-8")) ) return None nextval = "" for line in out.stdout.decode("utf-8").splitlines(): if line.strip() == "": continue if line.startswith("subject="): data["subject"] = line[8:].strip() elif line.startswith("issuer="): data["issuer"] = line[7:].strip() elif line.startswith("notBefore="): data["startdate"] = line[10:].strip() elif line.startswith("notAfter="): data["enddate"] = line[9:].strip() elif line.startswith("Modulus="): data["modulus"] = line[8:].strip() elif line.startswith("X509v3 Subject Key Identifier"): nextval = "subjectKeyIdentifier" elif line.startswith("X509v3 Authority Key Identifier"): nextval = "authorityKeyIdentifier" elif line.startswith(" "): if nextval == "subjectKeyIdentifier": data["subjectKeyIdentifier"] = line.strip().upper() elif nextval == "authorityKeyIdentifier" and line.startswith(" keyid:"): data["authorityKeyIdentifier"] = line[10:].strip().upper() elif "subject_hash" not in data: # subject_hash comes first without key to identify it data["subject_hash"] = line.strip() else: # second issue_hash without key to identify this value data["issuer_hash"] = line.strip() data["isca"] = isCA(cert) data["content"] = cert if data["subject"] == data["issuer"]: data["root"] = True # Some Root CAs might not have their authorityKeyIdentifier set to themself if data["isca"] and "authorityKeyIdentifier" not in data: data["authorityKeyIdentifier"] = data["subjectKeyIdentifier"] else: data["root"] = False return data # pylint: disable-next=invalid-name def getCertWithText(cert): # pylint: disable-next=subprocess-run-check out = subprocess.run( ["openssl", "x509", "-text"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=cert.encode("utf-8"), ) if out.returncode: # pylint: disable-next=consider-using-f-string log_error("Invalid Certificate: {}".format(out.stderr.decode("utf-8"))) return None return out.stdout.decode("utf-8") # pylint: disable-next=invalid-name def getPrivateKey(key): # set an invalid password to prevent asking in case of an encrypted one # pylint: disable-next=subprocess-run-check out = subprocess.run( ["openssl", "pkey", "-passin", "pass:invalid", "-text", "-noout"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=key.encode("utf-8"), ) if out.returncode: # pylint: disable-next=consider-using-f-string log_error("Invalid or encrypted Key: {}".format(out.stderr.decode("utf-8"))) return None return out.stdout.decode("utf-8") # pylint: disable-next=invalid-name def checkKeyBelongToCert(key, cert): # pylint: disable-next=subprocess-run-check out = subprocess.run( ["openssl", "pkey", "-pubout"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=key.encode("utf-8"), ) if out.returncode: # pylint: disable-next=consider-using-f-string log_error("Invalid Key: {}".format(out.stderr.decode("utf-8"))) raise CertCheckError("Invalid Key") # pylint: disable-next=invalid-name keyPubKey = out.stdout.decode("utf-8") # pylint: disable-next=subprocess-run-check out = subprocess.run( ["openssl", "x509", "-noout", "-pubkey"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=cert.encode("utf-8"), ) if out.returncode: # pylint: disable-next=consider-using-f-string log_error("Invalid Cert file: {}".format(out.stderr.decode("utf-8"))) raise CertCheckError("Invalid Certificate") # pylint: disable-next=invalid-name certPubKey = out.stdout.decode("utf-8") if keyPubKey != certPubKey: log_error("The provided key does not belong to the server certificate") # pylint: disable-next=consider-using-f-string log("{} vs. {}".format(keyPubKey, certPubKey), 1) raise CertCheckError("Key does not belong to Certificate") # pylint: disable-next=invalid-name def checkCompleteCAChain(server_cert_content, certData): # pylint: disable-next=invalid-name foundRootCA = False if len(certData.keys()) == 0: raise CertCheckError("No CAs found") # pylint: disable-next=invalid-name serverCertHash = None for h, data in certData.items(): if data["content"] == server_cert_content: # pylint: disable-next=invalid-name serverCertHash = h break if certData[serverCertHash]["isca"]: raise CertCheckError("Server Certificate must not be a CA") subject = certData[serverCertHash]["subject"] ihash = certData[serverCertHash]["issuer_hash"] # pylint: disable-next=invalid-name issuerKeyId = certData[serverCertHash]["authorityKeyIdentifier"] if not ihash or ihash not in certData: raise CertCheckError("No CA found for server certificate") cert = getCertWithText(certData[serverCertHash]["content"]) if not cert: raise CertCheckError("Unable to parse the server certificate") isValid( certData[serverCertHash]["startdate"], certData[serverCertHash]["enddate"], certData[serverCertHash]["subject"], ) while ihash in certData: # pylint: disable-next=invalid-name keyId = certData[ihash]["subjectKeyIdentifier"] if not (keyId and issuerKeyId and keyId == issuerKeyId): raise CertCheckError( # pylint: disable-next=consider-using-f-string "Incomplete CA Chain. Key Identifiers do not match. Unable to find issuer of '{}'".format( subject ) ) if not certData[ihash]["isca"]: raise CertCheckError("CA missing basic constraints extension") subject = certData[ihash]["subject"] nexthash = certData[ihash]["issuer_hash"] # pylint: disable-next=invalid-name issuerKeyId = certData[ihash]["authorityKeyIdentifier"] isValid(certData[ihash]["startdate"], certData[ihash]["enddate"], subject) if nexthash == ihash: # Found Root CA, we can exit # pylint: disable-next=invalid-name foundRootCA = True if not certData[ihash]["root"]: raise CertCheckError("Root CA has different issuer") break else: if certData[ihash]["root"]: raise CertCheckError("Intermediate CA has subject equals to issuer") ihash = nexthash if not foundRootCA: raise CertCheckError( # pylint: disable-next=consider-using-f-string "Incomplete CA Chain. Unable to find issuer of '{}'".format(subject) ) # pylint: disable-next=invalid-name def generateApacheCert(server_cert_content, certData): return generateCertWithChainFile(server_cert_content, certData) # pylint: disable-next=invalid-name def generateCertWithChainFile(serverCert, certData): # pylint: disable-next=invalid-name retContent = "" if len(certData.keys()) == 0: log_error("No CA found in Hash") return "" # pylint: disable-next=invalid-name serverCertHash = None for h, data in certData.items(): if data["content"] == serverCert: # pylint: disable-next=invalid-name serverCertHash = h break ihash = certData[serverCertHash]["issuer_hash"] if not ihash or ihash not in certData: log_error("No CA found for server certificate") return "" cert = getCertWithText(serverCert) if not cert: log_error("Unable to get the server certificate") return "" # pylint: disable-next=invalid-name retContent += cert while ihash in certData: nexthash = certData[ihash]["issuer_hash"] cert = getCertWithText(certData[ihash]["content"]) if not cert: return "" if nexthash == ihash: # Found Root CA, we can exit break ihash = nexthash # pylint: disable-next=invalid-name retContent += cert return retContent # pylint: disable-next=invalid-name def deployApache(apache_cert_content, server_key_content): if os.path.exists(APACHE_KEY_FILE): os.remove(APACHE_KEY_FILE) if os.path.exists(APACHE_CRT_FILE): os.remove(APACHE_CRT_FILE) # pylint: disable-next=unspecified-encoding with open(APACHE_KEY_FILE, "w") as f: f.write(server_key_content) os.chmod(APACHE_KEY_FILE, int("0600", 8)) # pylint: disable-next=unspecified-encoding with open(APACHE_CRT_FILE, "w") as f: f.write(apache_cert_content) # exists on server and proxy os.system("/usr/bin/spacewalk-setup-httpd") log( """After changing the server certificate please execute: $> spacewalk-service stop """ ) # pylint: disable-next=invalid-name def deployPg(server_key_content): pg_uid, pg_gid = getUidGid("postgres", "postgres") if pg_uid and pg_gid: # deploy only the key with different permissions # the certificate is the same as for apache if os.path.exists(PG_KEY_FILE): os.remove(PG_KEY_FILE) # pylint: disable-next=unspecified-encoding with open(PG_KEY_FILE, "w") as f: f.write(server_key_content) os.chmod(PG_KEY_FILE, int("0600", 8)) os.chown(PG_KEY_FILE, pg_uid, pg_gid) log("""$> systemctl restart postgresql.service """) # pylint: disable-next=invalid-name def deployCAInDB(certData): if not os.path.exists("/usr/bin/rhn-ssl-dbstore"): # not a Uyuni Server - skip deploying into DB return # pylint: disable-next=unused-variable for h, ca in certData.items(): if ca["root"]: # pylint: disable-next=subprocess-run-check out = subprocess.run( ["/usr/bin/rhn-ssl-dbstore", "--ca-cert", "-"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=ca["content"].encode("utf-8"), ) if out.returncode: log_error( # pylint: disable-next=consider-using-f-string "Failed to upload CA Certificate to DB: {}".format( out.stderr.decode("utf-8") ) ) raise OSError("Failed to upload CA Certificate to DB") break # pylint: disable-next=invalid-name def deployCAUyuni(certData): # pylint: disable-next=unused-variable for h, ca in certData.items(): if ca["root"]: if os.path.exists(os.path.join(ROOT_CA_HTTP_DIR, ROOT_CA_NAME)): os.remove(os.path.join(ROOT_CA_HTTP_DIR, ROOT_CA_NAME)) with open( os.path.join(ROOT_CA_HTTP_DIR, ROOT_CA_NAME), "w", encoding="utf-8" ) as f: f.write(ca["content"]) os.chmod(os.path.join(ROOT_CA_HTTP_DIR, ROOT_CA_NAME), int("0644", 8)) if os.path.exists(os.path.join(CA_TRUST_DIR, PKI_ROOT_CA_NAME)): os.remove(os.path.join(CA_TRUST_DIR, PKI_ROOT_CA_NAME)) with open( os.path.join(CA_TRUST_DIR, PKI_ROOT_CA_NAME), "w", encoding="utf-8" ) as f: f.write(ca["content"]) os.chmod(os.path.join(CA_TRUST_DIR, PKI_ROOT_CA_NAME), int("0644", 8)) # SALT_CA_DIR exists only on the server, ignore on proxies if os.path.exists(SALT_CA_DIR): if os.path.exists(os.path.join(SALT_CA_DIR, ROOT_CA_NAME)): os.remove(os.path.join(SALT_CA_DIR, ROOT_CA_NAME)) with open( os.path.join(SALT_CA_DIR, ROOT_CA_NAME), "w", encoding="utf-8" ) as f: f.write(ca["content"]) os.chmod(os.path.join(SALT_CA_DIR, ROOT_CA_NAME), int("0644", 8)) break # in case a systemd timer try to do the same time.sleep(3) os.system("/usr/share/rhn/certs/update-ca-cert-trust.sh") log( """$> spacewalk-service start As the CA certificate has been changed, please deploy the CA to all registered clients. On salt-managed clients, you can do this by applying the highstate.""" ) # pylint: disable-next=invalid-name def checks(server_key_content, server_cert_content, certData): """ Perform different checks on the input data """ if not getPrivateKey(server_key_content): raise CertCheckError("Unable to read the server key. Is it maybe encrypted?") checkKeyBelongToCert(server_key_content, server_cert_content) checkCompleteCAChain(server_cert_content, certData) # pylint: disable-next=invalid-name def getContainersSetup( root_ca_content, intermediate_ca_content, server_cert_content, server_key_content ): if not root_ca_content: raise CertCheckError("Root CA is required") if not server_cert_content: raise CertCheckError("Server Certificate is required") if not server_key_content: raise CertCheckError("Server Private Key is required") # pylint: disable-next=invalid-name certData = prepareData( root_ca_content, server_cert_content, intermediate_ca_content ) checks(server_key_content, server_cert_content, certData) apache_cert_content = generateApacheCert(server_cert_content, certData) if not apache_cert_content: raise CertCheckError("Failed to generate certificates") return apache_cert_content def _main(): """main routine""" options = processCommandline() checkOptions( options.root_ca_file, options.server_cert_file, options.server_key_file, options.intermediate_ca_file, ) files_content = readAllFiles( options.root_ca_file, options.server_cert_file, options.server_key_file, options.intermediate_ca_file, ) if options.check_only: getContainersSetup( files_content.root_ca, files_content.intermediate_cas, files_content.server_cert, files_content.server_key, ) sys.exit(0) # pylint: disable-next=invalid-name certData = prepareData( files_content.root_ca, files_content.server_cert, files_content.intermediate_cas ) checks(files_content.server_key, files_content.server_cert, certData) apache_cert_content = generateApacheCert(files_content.server_cert, certData) if not apache_cert_content: log_error("Failed to generate certificate for Apache") sys.exit(1) deployApache(apache_cert_content, files_content.server_key) deployPg(files_content.server_key) deployCAUyuni(certData) deployCAInDB(certData) def main(): """main routine wrapper (exception handler) 1 general error """ # pylint: disable-next=invalid-name def writeError(e): # pylint: disable-next=consider-using-f-string log_error("\nERROR: %s\n" % e) log(traceback.format_exc(None), 1) ret = 0 try: ret = _main() or 0 # pylint: disable-next=broad-exception-caught except Exception as e: writeError(e) ret = 1 return ret # ------------------------------------------------------------------------------- if __name__ == "__main__": sys.stderr.write( "\nWARNING: intended to be wrapped by another executable\n" " calling program.\n" ) sys.exit(abs(main() or 0)) # =============================================================================== 0707010000000E000081ED000003E80000006400000001662798DF0000070F000000000000000000000000000000000000002400000000spacewalk-certs-tools/rhn-bootstrap#!/usr/bin/python -u # # Copyright (c) 2008--2015 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # generate bootstrap scripts for the various up2date clients # (namely 2.x, 3.x and 4.x) # # Author: Todd Warner <taw@redhat.com> # ## language imports import sys import os # management decisions lead to funny code sys.argv[0] = os.path.join(os.path.dirname(sys.argv[0]), os.path.basename(sys.argv[0]).replace('mgr', 'rhn')) mod_name = os.path.basename(sys.argv[0]).replace('-', '_') try: mod = __import__("certs." + mod_name) except KeyboardInterrupt: sys.stderr.write("\nUser interrupted process.\n") sys.exit(0) except ImportError as e: sys.stderr.write("Unable to load module %s\n" % mod_name) sys.stderr.write(str(e) + "\n") sys.exit(1) mod = getattr(mod, mod_name) #------------------------------------------------------------------------------- if __name__ == '__main__': try: sys.exit(mod.main() or 0) except KeyboardInterrupt: sys.stderr.write("\nUser interrupted process.\n") sys.exit(0) except SystemExit: raise except: sys.stderr.write("\nERROR: unhandled exception occurred:\n") raise #=============================================================================== 0707010000000F000081ED000003E80000006400000001662798DF000007A2000000000000000000000000000000000000002300000000spacewalk-certs-tools/rhn-ssl-tool#!/usr/bin/python # # Copyright (c) 2008--2015 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # RHN SSL Maintenance Tool # # Generate and maintain SSL keys & certs. One can also build RPMs in the RHN # product context. # # NOTE: this tool is geared for RHN product usage, but can be used outside of # that context to some degree. # # Author: Todd Warner <taw@redhat.com> # ## language imports import sys import os # management decisions lead to funny code sys.argv[0] = os.path.join(os.path.dirname(sys.argv[0]), os.path.basename(sys.argv[0]).replace('mgr', 'rhn')) mod_name = os.path.basename(sys.argv[0]).replace('-', '_') try: mod = __import__("certs." + mod_name) except KeyboardInterrupt: sys.stderr.write("\nUser interrupted process.\n") sys.exit(0) except ImportError as e: sys.stderr.write("Unable to load module %s\n" % mod_name) sys.stderr.write(str(e) + "\n") sys.exit(1) mod = getattr(mod, mod_name) #------------------------------------------------------------------------------- if __name__ == '__main__': try: sys.exit(mod.main() or 0) except KeyboardInterrupt: sys.stderr.write("\nUser interrupted process.\n") sys.exit(0) except SystemExit: raise except: sys.stderr.write("\nERROR: unhandled exception occurred:\n") raise #=============================================================================== 07070100000010000081A4000003E80000006400000001662798DF00000072000000000000000000000000000000000000002700000000spacewalk-certs-tools/rhn-stunnel.confCAfile = /usr/share/rhn/RHNS-CA-CERT client = yes connect = xmlrpc.rhn.redhat.com:443 foreground = yes verify = 2 07070100000011000081ED000003E80000006400000001662798DF00000037000000000000000000000000000000000000002800000000spacewalk-certs-tools/rhn-sudo-ssl-tool#!/bin/bash /usr/bin/sudo /usr/bin/rhn-ssl-tool "$@" 07070100000012000081ED000003E80000006400000001662798DF00007514000000000000000000000000000000000000002700000000spacewalk-certs-tools/rhn_bootstrap.py#!/usr/bin/python -u # pylint: disable=missing-module-docstring # # Copyright (c) 2008--2014 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # generate bootstrap scripts for the various up2date clients # (namely 2.x, 3.x and 4.x) # # Author: Todd Warner <taw@redhat.com> # ## language imports from __future__ import print_function import os import sys # pylint: disable-next=unused-import import glob import socket import shutil import operator try: import urllib.parse as urlparse except ImportError: import urlparse # pylint: disable-next=deprecated-module,unused-import from optparse import Option, OptionParser, SUPPRESS_HELP ## local imports # pylint: disable-next=unused-import from uyuni.common import rhn_rpm from spacewalk.common.rhnConfig import CFG, initCFG from .rhn_bootstrap_strings import ( getHeader, getGPGKeyImportSh, getCorpCACertSh, getRegistrationStackSh, getRegistrationSaltSh, removeTLSCertificate, ) from .sslToolConfig import CA_CRT_NAME # pylint: disable-next=ungrouped-imports from uyuni.common.fileutils import rotateFile, cleanupAbsPath from uyuni.common.checksum import getFileChecksum try: # pylint: disable-next=ungrouped-imports from spacewalk.common.rhnConfig import PRODUCT_NAME # pylint: disable-next=bare-except except: PRODUCT_NAME = "SUSE Manager" ## GLOBALS if os.path.exists("/usr/share/rhn/proxy") or os.path.exists("/var/www/rhns/proxy"): MY_PRODUCT_NAME = PRODUCT_NAME + " Proxy" elif os.path.exists("/usr/share/rhn/server") or os.path.exists("/var/www/rhns/server"): MY_PRODUCT_NAME = PRODUCT_NAME + " Server" DEFAULT_CA_CERT_PATH = "/usr/share/rhn/" + CA_CRT_NAME initCFG("server") PUB_ROOT = CFG.DOCUMENTROOT initCFG("java") DEFAULT_APACHE_PUB_DIRECTORY = PUB_ROOT + "/pub" DEFAULT_OVERRIDES = "client-config-overrides.txt" DEFAULT_SCRIPT = "bootstrap.sh" # exit codes # pylint: disable-next=invalid-name errnoSuccess = 0 # pylint: disable-next=invalid-name errnoGeneral = 1 # pylint: disable-next=invalid-name errnoScriptNameClash = 10 # pylint: disable-next=invalid-name errnoBadScriptName = 11 # pylint: disable-next=invalid-name errnoExtraCommandLineArgs = 12 # pylint: disable-next=invalid-name errnoBadHttpProxyString = 13 # pylint: disable-next=invalid-name errnoBadPath = 14 # pylint: disable-next=invalid-name errnoNotFQDN = 15 # pylint: disable-next=invalid-name errnoCANotFound = 16 # pylint: disable-next=invalid-name errnoGPGNotFound = 17 # pylint: disable-next=invalid-name def _parseConfigLine(line): """parse a line from a config file. Format can be either "key=value\n" or "whatever text\n" return either: (key, value) or None The '\n' is always stripped from the value. """ kv = line.decode("utf8").split("=") if len(kv) < 2: # not a setting return None if len(kv) > 2: # '=' is part of the value, need to rejoin it. kv = [kv[0], "=".join(kv[1:])] if kv[0].find("[comment]") > 0: # comment; not a setting return None # it's a setting, trim the '\n' and return the (key, value) pair. kv[0] = kv[0].strip() kv[1] = kv[1].strip() return tuple(kv) # pylint: disable-next=invalid-name def readConfigFile(configFile): "read in config file, return dictionary of key/value pairs" fin = open(configFile, "rb") d = {} for line in fin.readlines(): kv = _parseConfigLine(line) if kv: d[kv[0]] = kv[1] return d # should come out of common code when we move this code out of # rhns-certs-tools # pylint: disable-next=invalid-name def parseUrl(url): """urlparse is more complicated than what we need. We make the assumption that the URL has real URL information. NOTE: http/https ONLY for right now. The normal behavior of urlparse: - if no {http[s],file}:// then the string is considered everything that normally follows the URL, e.g. /XMLRPC - if {http[s],file}:// exists, anything between that and the next / is the URL. The behavior of *this* function: - if no {http[s],file}:// then the string is simply assumed to be a URL without the {http[s],file}:// attached. The parsed info is reparsed as one would think it would be: - returns: (addressing scheme, network location, path, parameters, query, fragment identifier). NOTE: netloc (or network location) can be HOSTNAME:PORT """ schemes = ("http", "https") if url is None: return None parsed = list(urlparse.urlparse(url)) if not parsed[0] or parsed[0] not in schemes: url = "https://" + url parsed = list(urlparse.urlparse(url)) parsed[0] = "" return tuple(parsed) # pylint: disable-next=invalid-name def parseHttpProxyString(httpProxy): """parse HTTP proxy string and check for validity""" httpProxy = parseUrl(httpProxy)[1] tup = httpProxy.split(":") if len(tup) != 2: # pylint: disable-next=consider-using-f-string sys.stderr.write("ERROR: invalid host:port (%s)\n" % httpProxy) sys.exit(errnoBadHttpProxyString) try: int(tup[1]) except ValueError: # pylint: disable-next=consider-using-f-string sys.stderr.write("ERROR: invalid host:port (%s)\n" % httpProxy) sys.exit(errnoBadHttpProxyString) return httpProxy # pylint: disable-next=invalid-name def processCACertPath(options): if options.ssl_cert: if options.ssl_cert[-4:] == ".rpm": sys.stderr.write( "ERROR: SSL Certificate as rpm package not supported anymore" ) sys.exit(errnoCANotFound) if not options.ssl_cert: # look for the raw cert options.ssl_cert = os.path.join(options.pub_tree, CA_CRT_NAME) if not os.path.isfile(options.ssl_cert): options.ssl_cert = "" # pylint: disable-next=invalid-name def getDefaultOptions(): # pylint: disable-next=invalid-name _defopts = { "activation-keys": "", "overrides": DEFAULT_OVERRIDES, "script": DEFAULT_SCRIPT, "hostname": CFG.HOSTNAME if CFG.has_key("hostname") else socket.getfqdn(), "ssl-cert": "", # will trigger a search "gpg-key": "", "http-proxy": "", "http-proxy-username": "", "http-proxy-password": "", "allow-config-actions": 0, "allow-remote-commands": 0, "no-bundle": 0, "force-bundle": 0, "no-gpg": 0, "force": 0, "pub-tree": DEFAULT_APACHE_PUB_DIRECTORY, "verbose": 0, } return _defopts defopts = getDefaultOptions() # pylint: disable-next=invalid-name def getOptionsTable(): """returns the command line options table""" # pylint: disable-next=invalid-name def getSetString(value): if value: return "SET" return "UNSET" # the options # pylint: disable-next=invalid-name bsOptions = [ Option( "--activation-keys", action="store", type="string", default=defopts["activation-keys"], # pylint: disable-next=consider-using-f-string help="activation key as defined in the web UI - only 1 key is allowed now (currently: %s)" % repr(defopts["activation-keys"]), ), Option( "--overrides", action="store", type="string", default=defopts["overrides"], # pylint: disable-next=consider-using-f-string help="configuration overrides filename (currently: %s)" % defopts["overrides"], ), Option( "--script", action="store", type="string", default=defopts["script"], # pylint: disable-next=consider-using-f-string help="bootstrap script filename. (currently: %s)" % defopts["script"], ), Option( "--hostname", action="store", type="string", default=defopts["hostname"], # pylint: disable-next=consider-using-f-string help="hostname (FQDN) to which clients connect (currently: %s)" % defopts["hostname"], ), Option( "--ssl-cert", action="store", type="string", default=defopts["ssl-cert"], help='path to corporate public SSL certificate - an RPM or a raw certificate. It will be copied to --pub-tree. A value of "" will force a search of --pub-tree.', ), Option( "--gpg-key", action="store", type="string", default=defopts["gpg-key"], # pylint: disable-next=consider-using-f-string help="path to corporate public GPG key, if used. It will be copied to the location specified by the --pub-tree option. Format is GPG_KEY1,GPG_KEY2 (currently: %s)" % repr(defopts["gpg-key"]), ), Option( "--http-proxy", action="store", type="string", default=defopts["http-proxy"], # pylint: disable-next=consider-using-f-string help='HTTP proxy setting for the clients - hostname:port. --http-proxy="" disables. (currently: %s)' % repr(defopts["http-proxy"]), ), Option( "--http-proxy-username", action="store", type="string", default=defopts["http-proxy-username"], # pylint: disable-next=consider-using-f-string help='if using an authenticating HTTP proxy, specify a username. --http-proxy-username="" disables. (currently: %s)' % repr(defopts["http-proxy-username"]), ), Option( "--http-proxy-password", action="store", type="string", default=defopts["http-proxy-password"], # pylint: disable-next=consider-using-f-string help="if using an authenticating HTTP proxy, specify a password. (currently: %s)" % repr(defopts["http-proxy-password"]), ), Option( "--no-bundle", action="store_true", # pylint: disable-next=consider-using-f-string help="boolean; avoid installing salt minion bundle (venv-salt-minion) instead of salt minion (currently %s)" % getSetString(defopts["no-bundle"]), ), Option( "--force-bundle", action="store_true", # pylint: disable-next=consider-using-f-string help="boolean; Force installing salt minion bundle (venv-salt-minion) instead of salt minion (currently %s)" % getSetString(defopts["force-bundle"]), ), Option( "--no-gpg", action="store_true", # pylint: disable-next=consider-using-f-string help="(not recommended) boolean; turn off GPG checking by the clients (currently %s)" % getSetString(defopts["no-gpg"]), ), Option( "--pub-tree", action="store", type="string", default=defopts["pub-tree"], # pylint: disable-next=consider-using-f-string help="(change not recommended) public directory tree where the CA SSL cert/cert-RPM will land as well as the bootstrap directory and scripts. (currently %s)" % defopts["pub-tree"], ), Option( "--force", action="store_true", # pylint: disable-next=consider-using-f-string help="(not recommended) boolean; including this option forces bootstrap script generation despite warnings (currently %s)" % getSetString(defopts["force"]), ), Option( "-v", "--verbose", action="count", # pylint: disable-next=consider-using-f-string help='be verbose - accumulable: -vvv means "be *really* verbose" (currently %s)' % defopts["verbose"], ), ] return bsOptions # pylint: disable-next=invalid-name def parseCommandline(): "parse the commandline/options, sanity checking, et c." # pylint: disable-next=invalid-name _progName = "mgr-bootstrap" # pylint: disable-next=invalid-name,consider-using-f-string _usage = """\ %s [options] Note: for mgr-bootstrap to work, certain files are expected to be in %s/ (the default Apache public directory): - the CA SSL public certificate (probably RHN-ORG-TRUSTED-SSL-CERT) """ % ( _progName, DEFAULT_APACHE_PUB_DIRECTORY, ) # preliminary parse (-h/--help is acted upon during final parse) # pylint: disable-next=invalid-name optionList = getOptionsTable() # pylint: disable-next=invalid-name optionListNoHelp = optionList[:] fake_help = Option("-h", "--help", action="count", help="") optionListNoHelp.append(fake_help) # pylint: disable-next=invalid-name options, _args = OptionParser( option_list=optionListNoHelp, add_help_option=0 ).parse_args() # we take no extra commandline arguments that are not linked to an option if _args: sys.stderr.write( # pylint: disable-next=consider-using-f-string "\nERROR: these arguments make no sense in this " "context (try --help): %s\n" % repr(_args) ) sys.exit(errnoExtraCommandLineArgs) # reset the defaults - I need them on the next pass global defopts defopts = { "activation-keys": options.activation_keys, "overrides": options.overrides or DEFAULT_OVERRIDES, "script": options.script or DEFAULT_SCRIPT, "hostname": options.hostname, "ssl-cert": options.ssl_cert, "gpg-key": options.gpg_key, "http-proxy": options.http_proxy, "http-proxy-username": options.http_proxy_username, "http-proxy-password": options.http_proxy, # pylint: disable-next=unnecessary-negation "no-bundle": not not options.no_bundle, # pylint: disable-next=unnecessary-negation "force-bundle": not not options.force_bundle, # pylint: disable-next=unnecessary-negation "no-gpg": not not options.no_gpg, "pub-tree": options.pub_tree, "force": options.force, "verbose": options.verbose or 0, } processCACertPath(options) defopts["ssl-cert"] = options.ssl_cert # final parse after defaults have been remapped # pylint: disable-next=invalid-name options, _args = OptionParser( option_list=getOptionsTable(), usage=_usage ).parse_args() return options # pylint: disable-next=invalid-name def processCommandline(): options = parseCommandline() if options.script[-3:] != ".sh": sys.stderr.write( # pylint: disable-next=consider-using-f-string """\ ERROR: value of --script must end in '.sh': '%s'\n""" % options.script ) if not options.force: sys.stderr.write("exiting\n") sys.exit(errnoBadScriptName) options.pub_tree = cleanupAbsPath(options.pub_tree or DEFAULT_APACHE_PUB_DIRECTORY) options.overrides = os.path.basename(options.overrides) options.script = os.path.basename(options.script) if options.pub_tree.find(DEFAULT_APACHE_PUB_DIRECTORY) != 0: sys.stderr.write( "WARNING: it's *highly* suggested that --pub-tree is set to:\n" ) # pylint: disable-next=consider-using-f-string sys.stderr.write(" %s\n" % DEFAULT_APACHE_PUB_DIRECTORY) sys.stderr.write(" It is currently set to:\n") # pylint: disable-next=consider-using-f-string sys.stderr.write(" %s\n" % options.pub_tree) if not options.force: sys.stderr.write("exiting\n") sys.exit(errnoBadPath) if options.overrides == options.script: sys.stderr.write( # pylint: disable-next=consider-using-f-string """\ ERROR: the value of --overrides and --script cannot be the same! '%s'\n""" % options.script ) sys.exit(errnoScriptNameClash) if len(options.hostname.split(".")) < 3: msg = ( # pylint: disable-next=consider-using-f-string "WARNING: --hostname (%s) doesn't appear to be a FQDN.\n" % options.hostname ) sys.stderr.write(msg) if not options.force: sys.stderr.write("exiting\n") sys.exit(errnoNotFQDN) processCACertPath(options) if options.ssl_cert and not os.path.exists(options.ssl_cert): sys.stderr.write("ERROR: CA SSL certificate file not found\n") sys.exit(errnoCANotFound) if not options.no_gpg and options.gpg_key: for gpg_key in options.gpg_key.split(","): if not os.path.exists(gpg_key): sys.stderr.write( # pylint: disable-next=consider-using-f-string "ERROR: corporate public GPG key file '{0}' not found\n".format( gpg_key ) ) sys.exit(errnoGPGNotFound) if options.http_proxy != "": options.http_proxy = parseHttpProxyString(options.http_proxy) if not options.http_proxy: options.http_proxy_username = "" if not options.http_proxy_username: options.http_proxy_password = "" # forcing numeric values for opt in ["force_bundle", "no_bundle", "no_gpg", "verbose"]: # operator.truth should return (0, 1) or (False, True) depending on # the version of python; passing any of those values through int() # will return an int val = int(operator.truth(getattr(options, opt))) setattr(options, opt, val) return options # pylint: disable-next=invalid-name def copyFiles(options): """copies SSL cert and GPG key to --pub-tree if not in there already existence check should have already been done. """ # pylint: disable-next=invalid-name pubDir = cleanupAbsPath(options.pub_tree or DEFAULT_APACHE_PUB_DIRECTORY) # pylint: disable-next=invalid-name def copyFile(file0, file1): if not os.path.exists(os.path.dirname(file1)): sys.stderr.write( # pylint: disable-next=consider-using-f-string "ERROR: directory does not exist:\n %s\n" % os.path.dirname(file1) ) sys.exit(errnoBadPath) if not os.path.exists(file0): # pylint: disable-next=consider-using-f-string sys.stderr.write("ERROR: file does not exist:\n %s\n" % file0) sys.exit(errnoCANotFound) sys.stderr.write( # pylint: disable-next=consider-using-f-string """\ Coping file into public directory tree: %s to %s """ % (file0, file1) ) shutil.copy(file0, file1) # CA SSL cert if options.ssl_cert: # pylint: disable-next=invalid-name writeYN = 1 dest = os.path.join(pubDir, os.path.basename(options.ssl_cert)) if os.path.dirname(options.ssl_cert) != pubDir: if os.path.isfile(dest) and getFileChecksum( "md5", options.ssl_cert ) != getFileChecksum("md5", dest): rotateFile(dest, options.verbose) elif os.path.isfile(dest): # pylint: disable-next=invalid-name writeYN = 0 if writeYN: copyFile(options.ssl_cert, dest) # corp GPG keys if not options.no_gpg and options.gpg_key: for gpg_key in options.gpg_key.split(","): # pylint: disable-next=invalid-name writeYN = 1 dest = os.path.join(pubDir, os.path.basename(gpg_key)) if os.path.dirname(gpg_key) != pubDir: if os.path.isfile(dest) and getFileChecksum( "md5", gpg_key ) != getFileChecksum("md5", dest): rotateFile(dest, options.verbose) elif os.path.isfile(dest): # pylint: disable-next=invalid-name writeYN = 0 if writeYN: copyFile(gpg_key, dest) # pylint: disable-next=invalid-name def writeClientConfigOverrides(options): """write our "overrides" configuration file This generated file is a configuration mapping file that is used to map settings in up2date and rhn_register when run through a seperate script. """ # pylint: disable-next=invalid-name up2dateConfMap = { # some are directly mapped, others are handled more delicately "http_proxy": "httpProxy", "http_proxy_username": "proxyUser", "http_proxy_password": "proxyPassword", "hostname": "serverURL", "ssl_cert": "sslCACert", "no_gpg": "useGPG", } # pylint: disable-next=invalid-name _bootstrapDir = cleanupAbsPath(os.path.join(options.pub_tree, "bootstrap")) if not os.path.exists(_bootstrapDir): # pylint: disable-next=consider-using-f-string print("* creating '%s'" % _bootstrapDir) os.makedirs(_bootstrapDir) # permissions should be fine d = {} if options.hostname: d["serverURL"] = "https://" + options.hostname + "/XMLRPC" # if proxy, enable it # if "", disable it if options.http_proxy: d["enableProxy"] = "1" d[up2dateConfMap["http_proxy"]] = options.http_proxy else: d["enableProxy"] = "0" d[up2dateConfMap["http_proxy"]] = "" # if proxy username, enable auth proxy # if "", disable it if options.http_proxy_username: d["enableProxyAuth"] = "1" d[up2dateConfMap["http_proxy_username"]] = options.http_proxy_username d[up2dateConfMap["http_proxy_password"]] = options.http_proxy_password else: d["enableProxyAuth"] = "0" d[up2dateConfMap["http_proxy_username"]] = "" d[up2dateConfMap["http_proxy_password"]] = "" processCACertPath(options) if not options.ssl_cert: sys.stderr.write( # pylint: disable-next=consider-using-f-string "WARNING: no SSL CA certificate found in %s\n" % options.pub_tree ) # pylint: disable-next=invalid-name _certname = os.path.basename(options.ssl_cert) or CA_CRT_NAME # pylint: disable-next=invalid-name _certdir = os.path.dirname(DEFAULT_CA_CERT_PATH) d[up2dateConfMap["ssl_cert"]] = os.path.join(_certdir, _certname) d[up2dateConfMap["no_gpg"]] = int(operator.truth(not options.no_gpg)) # pylint: disable-next=invalid-name writeYN = 1 # pylint: disable-next=invalid-name _overrides = cleanupAbsPath(os.path.join(_bootstrapDir, options.overrides)) if os.path.exists(_overrides): if readConfigFile(_overrides) != d: # only back it up if different backup = rotateFile(_overrides, depth=5, verbosity=options.verbose) if backup and options.verbose >= 0: print( """\ * WARNING: if there were hand edits to the rotated (backed up) file, some settings may need to be migrated.""" ) else: # exactly the same... no need to write # pylint: disable-next=invalid-name writeYN = 0 print( # pylint: disable-next=consider-using-f-string """\ * client configuration overrides (old and new are identical; not written): '%s'\n""" % _overrides ) if writeYN: # pylint: disable-next=unspecified-encoding fout = open(_overrides, "w") # header fout.write( """\ # RHN Client (rhn_register/up2date) config-overrides file v4.0 # # This file was autogenerated. # # The simple rules: # - a setting explicitely overwrites the setting in # /etc/syconfig/rhn/{rhn_register,up2date} on the client system. # - if a setting is removed, the client's state for that setting remains # unchanged. """ ) keys = list(d.keys()) keys.sort() for key in keys: if d[key] is not None: # pylint: disable-next=consider-using-f-string fout.write("%s=%s\n" % (key, d[key])) fout.close() print( # pylint: disable-next=consider-using-f-string """\ * bootstrap overrides (written): '%s'\n""" % _overrides ) if options.verbose >= 0: print("Values written:") for k, v in list(d.items()): print(k + " " * (25 - len(k)) + repr(v)) # pylint: disable-next=invalid-name def generateBootstrapScript(options): "write, copy and place files into <DEFAULT_APACHE_PUB_DIRECTORY>/bootstrap/" # pylint: disable-next=invalid-name orgCACert = os.path.basename(options.ssl_cert or "") # write to <DEFAULT_APACHE_PUB_DIRECTORY>/bootstrap/<options.overrides> writeClientConfigOverrides(options) processCACertPath(options) pubname = os.path.basename(options.pub_tree) # pylint: disable-next=invalid-name newScript = [] # generate script # In processCommandline() we have turned all boolean values to 0 or 1 # this means that we can negate those booleans with 1 - their current # value (instead of doing not value which can yield True/False, which # would print as such) newScript.append( getHeader( MY_PRODUCT_NAME, options, orgCACert, pubname, DEFAULT_APACHE_PUB_DIRECTORY ) ) # pylint: disable-next=invalid-name writeYN = 1 newScript.append(getGPGKeyImportSh()) newScript.append(getCorpCACertSh()) # SLES: install packages required for registration on systems that do not have them installed newScript.append(getRegistrationStackSh()) newScript.append(removeTLSCertificate()) newScript.append(getRegistrationSaltSh(MY_PRODUCT_NAME)) # pylint: disable-next=invalid-name _bootstrapDir = cleanupAbsPath(os.path.join(options.pub_tree, "bootstrap")) # pylint: disable-next=invalid-name _script = cleanupAbsPath(os.path.join(_bootstrapDir, options.script)) # pylint: disable-next=invalid-name newScript = "".join(newScript) if os.path.exists(_script): # pylint: disable-next=invalid-name,unspecified-encoding oldScript = open(_script, "r").read() if oldScript == newScript: # pylint: disable-next=invalid-name writeYN = 0 elif os.path.exists(_script): backup = rotateFile(_script, depth=5, verbosity=options.verbose) if backup and options.verbose >= 0: # pylint: disable-next=consider-using-f-string print("* rotating %s --> %s" % (_script, backup)) del oldScript if writeYN: # pylint: disable-next=unspecified-encoding fout = open(_script, "w") fout.write(newScript) fout.close() print( # pylint: disable-next=consider-using-f-string """\ * bootstrap script (written): '%s'\n""" % _script ) else: print( # pylint: disable-next=consider-using-f-string """\ * boostrap script (old and new scripts identical; not written): '%s'\n""" % _script ) def main(): """Main code block: o options on commandline take precedence, but if option not set... o prepopulate the commandline options from already generated <DEFAULT_APACHE_PUB_DIRECTORY>/bootstrap/client-config-overrides.txt if in existance. FIXME: isn't done as of yet. o set defaults otherwise """ if "--salt" in sys.argv: sys.stderr.write("-" * 65) sys.stderr.write("\n") sys.stderr.write( "DEPRECATION WARNING:\n" '\tThe option "--salt" is default and has been deprecated.\n' "\tThis option should not be specified anymore." "\tIt will be not recognized in the next release!\n" ) sys.stderr.write("-" * 65) sys.stderr.write("\n") options = processCommandline() copyFiles(options) generateBootstrapScript(options) return 0 if __name__ == "__main__": # pylint: disable-next=pointless-string-statement """Exit codes - defined at top of module: errnoSuccess = 0 errnoGeneral = 1 errnoScriptNameClash = 10 errnoBadScriptName = 11 errnoExtraCommandLineArgs = 12 errnoBadHttpProxyString = 13 errnoBadPath = 14 errnoNotFQDN = 15 errnoCANotFound = 16 errnoGPGNotFound = 17 """ try: sys.exit(abs(main() or errnoSuccess)) except SystemExit: # No problem, sys.exit() raises this raise except KeyboardInterrupt: sys.exit(errnoSuccess) except ValueError as e: raise # should exit with a 1 (errnoGeneral) except Exception: sys.stderr.write("Unhandled ERROR occurred.\n") raise # should exit with a 1 (errnoGeneral) 07070100000013000081A4000003E80000006400000001662798DF000097B5000000000000000000000000000000000000002F00000000spacewalk-certs-tools/rhn_bootstrap_strings.py# pylint: disable=missing-module-docstring,anomalous-backslash-in-string # # Copyright (c) 2008--2018 Red Hat, Inc. # Copyright (c) 2016--2021 SUSE LLC. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # shell script function library for rhn-bootstrap # from uyuni.common.context_managers import cfg_component from spacewalk.common.rhnConfig import isUyuni import os.path _header = """\ #!/bin/bash # In case the script is executed using different interpreter than bash # then we call the script explicitely using bash SHPATH=$(readlink /proc/$$/exe) if ! [ "$SHPATH" = "/bin/bash" -o "$SHPATH" = "/usr/bin/bash" ]; then exec bash "$0" "$@" fi echo "{productName} Client bootstrap script v{version}" # This file was autogenerated. Minor manual editing of this script may be # necessary to complete the bootstrap setup. Once customized, the bootstrap # script can be triggered in one of two ways (the first is preferred): # # (1) centrally, from the {productName} via ssh (i.e., from the # {productName}): # cd {apachePubDirectory}/bootstrap/ # cat bootstrap-<edited_name>.sh | ssh root@<client-hostname> /bin/bash # # ...or... # # (2) in a decentralized manner, executed on each client, via wget or curl: # wget -qO- https://{hostname}/pub/bootstrap/bootstrap-<edited_name>.sh | /bin/bash # ...or... # curl -Sks https://{hostname}/pub/bootstrap/bootstrap-<edited_name>.sh | /bin/bash # SECURITY NOTE: # Use of these scripts via the two methods discussed is the most expedient # way to register machines to your {productName}. Since "wget" is used # throughout the script to download various files, a "Man-in-the-middle" # attack is theoretically possible. # # The actual registration process is performed securely via SSL, so the risk # is minimized in a sense. This message merely serves as a warning. # Administrators need to appropriately weigh their concern against the # relative security of their internal network. # PROVISIONING/KICKSTART NOTE: # If provisioning a client, ensure the proper CA SSL public certificate is # configured properly in the post section of your kickstart profiles (the # {productName} or hosted web user interface). # REGISTER VERSIONING NOTE: # This script will not work with traditional spacewalk registration tools. echo echo echo "MINOR MANUAL EDITING OF THIS FILE MAY BE REQUIRED!" echo echo "If this bootstrap script was created during the initial installation" echo "of a {productName}, the ACTIVATION_KEYS, and ORG_GPG_KEY values will" echo "probably *not* be set (see below). If this is the case, please do the" echo "following:" echo " - copy this file to a name specific to its use." echo " (e.g., to bootstrap-SOME_NAME.sh - like bootstrap-web-servers.sh.)" echo " - on the website create an activation key or keys for the system(s) to" echo " be registered." echo " - edit the values of the VARIABLES below (in this script) as" echo " appropriate:" echo " - ACTIVATION_KEYS needs to reflect the activation key(s) value(s)" echo " from the website. XKEY or XKEY,YKEY" echo " Please note that if you are using this script to boostrap minions," echo " only the FIRST activation key will be used. Multiple activation keys" echo " are not supported with salt" echo " - ORG_GPG_KEY needs to be set to the name(s) of the corporate public" echo " GPG key filename(s) (residing in {apachePubDirectory}) if appropriate. XKEY or XKEY,YKEY" echo " - When reactivating Salt minion, use REACTIVATION_KEY variable" echo " Consider using environmental variable REACTIVATION_KEY for single use reactivation keys." echo # can be edited, but probably correct (unless created during initial install): # NOTE: ACTIVATION_KEYS *must* be used to bootstrap a client machine. ACTIVATION_KEYS={activation_keys} ORG_GPG_KEY={org_gpg_key} # To reactivate single Salt client use following variable: # NOTE: Reactivation keys are removed valid only for single use. # It is also possible to use REACTIVATION_KEY environmental variable. REACTIVATION_KEY=${{REACTIVATION_KEY:-}} # can be edited, but probably correct: CLIENT_OVERRIDES={overrides} HOSTNAME={hostname} ORG_CA_CERT={orgCACert} USING_SSL={using_ssl} USING_GPG={using_gpg} REGISTER_THIS_BOX=1 # Set if you want to specify profilename for client systems. # NOTE: Make sure it's set correctly if any external command is used. # # ex. PROFILENAME="foo.example.com" # For specific client system # PROFILENAME=`hostname -s` # Short hostname # PROFILENAME=`hostname -f` # FQDN PROFILENAME="" # Empty by default to let it be set automatically. # SUSE Manager Specific settings: # # - Alternate location of the client tool repos providing # packages required for registration. Unless they are already installed on the # client this repo is expected to provide them: # ${{CLIENT_REPOS_ROOT}}/sle/VERSION/PATCHLEVEL # If empty, the SUSE Manager repositories provided at https://${{HOSTNAME}}/pub/repositories # are used. CLIENT_REPOS_ROOT= {venv_section} # Automatically schedule reboot of the machine in case of running transactional # system (for example SLE Micro) SCHEDULE_REBOOT_AFTER_TRANSACTION=1 # # ----------------------------------------------------------------------------- # DO NOT EDIT BEYOND THIS POINT ----------------------------------------------- # ----------------------------------------------------------------------------- # VENV_ENABLED=0 # # do not try to register a SUSE Manager server at itself # MYNAME=`hostname -f` LCMYNAME=`echo $MYNAME | tr '[:upper:]' '[:lower:]'` LCHOSTNAME=`echo $HOSTNAME | tr '[:upper:]' '[:lower:]'` if [ "$LCMYNAME" == "$LCHOSTNAME" ]; then echo "Name of client and of SUSE Manager server are the same." echo "Do not try to register a SUSE Manager server at itself!" echo "Aborting." exit 1 fi # an idea from Erich Morisse (of Red Hat). # use either wget *or* curl # Also check to see if the version on the # machine supports the insecure mode and format # command accordingly. if [ -x /usr/bin/wget ]; then output=`LANG=en_US /usr/bin/wget --no-check-certificate 2>&1` error=`echo $output | grep "unrecognized option"` if [ -z "$error" ]; then FETCH="/usr/bin/wget -nv -r -nd --no-check-certificate" else FETCH="/usr/bin/wget -nv -r -nd" fi elif [ -x /usr/bin/curl ]; then output=`LANG=en_US /usr/bin/curl -k 2>&1` error=`echo $output | grep "is unknown"` if [ -z "$error" ]; then FETCH="/usr/bin/curl -ksSOf" else FETCH="/usr/bin/curl -sSOf" fi else echo "To be able to download files, please install either 'wget' or 'curl'" exit 1 fi HTTP_PUB_DIRECTORY=http://${{HOSTNAME}}/{pubname} HTTPS_PUB_DIRECTORY=https://${{HOSTNAME}}/{pubname} if [ $USING_SSL -eq 0 ]; then HTTPS_PUB_DIRECTORY=${{HTTP_PUB_DIRECTORY}} fi INSTALLER=zypper # the order matters: see bsc#1222347 if [ -x /usr/bin/dnf ]; then INSTALLER=yum elif [ -x /usr/bin/yum ]; then INSTALLER=yum elif [ -x /usr/bin/zypper ]; then INSTALLER=zypper elif [ -x /usr/bin/apt ]; then INSTALLER=apt fi SNAPSHOT_ID="" function call_tukit() {{ tukit -q call $SNAPSHOT_ID /bin/bash <<< $@ }} function new_transaction() {{ if [ -n "$SNAPSHOT_ID" ]; then tukit -q close $SNAPSHOT_ID fi SNAPSHOT_ID=$(/usr/sbin/tukit -q open | sed 's/ID: \([0-9]*\)/\\1/') if [ -z "$SNAPSHOT_ID" ]; then echo "Transactional system detected, but could not open new transaction. Aborting!" exit 1 fi }} if [ -x /usr/sbin/tukit ]; then new_transaction echo "Transactional system detected. Reboot will be required to finish bootstrapping" fi if [ ! -w . ]; then echo "" echo "*** ERROR: $(pwd):" echo " No permission to write to the current directory." echo " Please execute this script in a directory where downloaded files can be stored." echo "" exit 1 fi """ # pylint: disable-next=invalid-name def getHeader(productName, options, orgCACert, pubname, apachePubDirectory): # 11/22/16 options.gpg_key is now a comma-separated list of path. # Removing paths from options.gpg_key org_gpg_key = ",".join( [os.path.basename(gpg_key) for gpg_key in options.gpg_key.split(",")] ) # pylint: disable-next=invalid-name with cfg_component("web") as CFG: version = CFG.version if isUyuni(): version = CFG.uyuni venv_section = ( # pylint: disable-next=consider-using-f-string """ # Avoid installing venv-salt-minion instead salt-minion # even if it available in the bootstrap repo AVOID_VENV_SALT_MINION={avoid_venv} # Force installing venv-salt-minion instead salt-minion # even if it is NOT available in the bootstrap repo FORCE_VENV_SALT_MINION={force_venv} """.format( avoid_venv=1 if bool(options.no_bundle) else 0, force_venv=1 if bool(options.force_bundle) else 0, ) or "" ) return _header.format( productName=productName, version=version, apachePubDirectory=apachePubDirectory, activation_keys=options.activation_keys, org_gpg_key=org_gpg_key, overrides=options.overrides, hostname=options.hostname, orgCACert=orgCACert, venv_section=venv_section, using_ssl=1, using_gpg=0 if bool(options.no_gpg) else 1, pubname=pubname, ) # pylint: disable-next=invalid-name def getRegistrationStackSh(): """ Determines which packages and repositories needs to be installed in order to register this system against SUMa server. """ # pylint: disable-next=invalid-name PKG_NAME = ["salt", "salt-minion"] # pylint: disable-next=invalid-name PKG_NAME_YUM = ["salt", "salt-minion"] # pylint: disable-next=invalid-name PKG_NAME_VENV = ["venv-salt-minion"] # pylint: disable-next=invalid-name PKG_NAME_UPDATE = list(PKG_NAME) PKG_NAME_UPDATE.extend(["zypper", "openssl"]) # pylint: disable-next=invalid-name PKG_NAME_VENV_UPDATE = list(PKG_NAME_VENV) PKG_NAME_VENV_UPDATE.extend(["zypper", "openssl"]) # pylint: disable-next=invalid-name PKG_NAME_UPDATE_YUM = list(PKG_NAME_YUM) PKG_NAME_UPDATE_YUM.extend(["yum", "openssl"]) # pylint: disable-next=invalid-name PKG_NAME_VENV_UPDATE_YUM = list(PKG_NAME_VENV) PKG_NAME_VENV_UPDATE_YUM.extend(["yum", "openssl"]) # pylint: disable-next=invalid-name TEST_VENV_FUNC = """ function test_venv_enabled() { if [ $FORCE_VENV_SALT_MINION -eq 1 ]; then VENV_ENABLED=1 elif [ $AVOID_VENV_SALT_MINION -ne 1 ]; then local repourl="$CLIENT_REPO_URL" if [ "$INSTALLER" == "zypper" ] || [ "$INSTALLER" == "yum" ]; then ARCH=$(rpm --eval "%{_arch}") else ARCH=$(dpkg --print-architecture) fi VENV_FILE="venv-enabled-$ARCH.txt" $FETCH $repourl/$VENV_FILE if [ -f "$VENV_FILE" ]; then echo "Bootstrap repo '$repourl' contains salt bundle." repourl="" VENV_ENABLED=1 fi rm -f "$VENV_FILE" fi } """ # pylint: disable-next=invalid-name TEST_VENV_CALL = """ test_venv_enabled """ return """\ echo echo "CLEANING UP OLD SUSE MANAGER REPOSITORIES" echo "-------------------------------------------------" function clean_up_old_trad_repos() {{ local trad_client_repo_prefix="spacewalk:" if [ -f /usr/bin/realpath ]; then GET_PATH="/usr/bin/realpath" else GET_PATH="/usr/bin/readlink -f --" fi for file in $1/$trad_client_repo_prefix*.repo; do if [ -f "$file" ]; then echo "Removing $($GET_PATH "$file")" rm -f $($GET_PATH "$file") fi done }} function clean_up_old_salt_repos() {{ if [ -f "$1" ]; then echo "Removing $1" rm -f "$1" fi }} function clean_up_old_repos() {{ clean_up_old_salt_repos "/etc/zypp/repos.d/susemanager:channels.repo" clean_up_old_salt_repos "/etc/yum.repos.d/susemanager:channels.repo" clean_up_old_salt_repos "/etc/apt/sources.list.d/susemanager:channels.list" clean_up_old_trad_repos "/etc/zypp/repos.d" clean_up_old_trad_repos "/etc/yum.repos.d" }} clean_up_old_repos echo echo "CHECKING THE REGISTRATION STACK" echo "-------------------------------------------------" function test_repo_exists() {{ local repourl="$CLIENT_REPO_URL" $FETCH $repourl/repodata/repomd.xml if [ ! -f "repomd.xml" ]; then echo "Bootstrap repo '$repourl' does not exist." repourl="" CLIENT_REPO_URL="" fi rm -f repomd.xml }} {TEST_VENV_FUNC} function setup_bootstrap_repo() {{ local repopath="$CLIENT_REPO_FILE" local reponame="$CLIENT_REPO_NAME" local repourl="$CLIENT_REPO_URL" test_repo_exists if [ -n "$CLIENT_REPO_URL" ]; then echo " adding client software repository at $repourl" cat <<EOF >"$repopath" [$reponame] name=$reponame baseurl=$repourl enabled=1 autorefresh=1 keeppackages=0 gpgcheck=0 EOF fi # Avoid modularity failsafe mechanism in dnf 4.2.7 or greater if [ -n "$Y_CLIENT_CODE_VERSION" ] && [ $Y_CLIENT_CODE_VERSION -ge 8 ]; then echo " adding 'module_hotfixes' flag to the repository config" echo "module_hotfixes=1" >> "$repopath" fi }} function remove_bootstrap_repo() {{ local repopath="$CLIENT_REPO_FILE" rm -f $repopath }} if [ "$INSTALLER" == yum ]; then function getY_CLIENT_CODE_BASE() {{ local BASE="" local VERSION="" # SLES ES6 is a special case; it will install a symlink named # centos-release pointing to redhat-release which will make the # original test fail; reverting the checks does not help as this # will break genuine CentOS systems. So use the poor man's approach # to detect this special case. SLES ES7 does not have this issue # https://bugzilla.suse.com/show_bug.cgi?id=1132576 # https://bugzilla.suse.com/show_bug.cgi?id=1152795 if [ -L /usr/share/doc/sles_es-release ]; then BASE="res" VERSION=6 elif [ -f /etc/almalinux-release ]; then grep -v '^#' /etc/almalinux-release | grep -q '\(AlmaLinux\)' && BASE="almalinux" VERSION=`grep -v '^#' /etc/almalinux-release | grep -Po '(?<=release )\d+'` elif [ -f /etc/rocky-release ]; then grep -v '^#' /etc/rocky-release | grep -q '\(Rocky Linux\)' && BASE="rockylinux" VERSION=`grep -v '^#' /etc/rocky-release | grep -Po '(?<=release )\d+'` elif [ -f /etc/oracle-release ]; then grep -v '^#' /etc/oracle-release | grep -q '\(Oracle\)' && BASE="oracle" VERSION=`grep -v '^#' /etc/oracle-release | grep -Po '(?<=release )\d+'` elif [ -f /etc/alinux-release ]; then grep -v '^#' /etc/alinux-release | grep -q '\(Alibaba\)' && BASE="alibaba" VERSION=`grep -v '^#' /etc/alinux-release | grep -Po '(?<=release )\d+'` elif [ -f /etc/centos-release ]; then grep -v '^#' /etc/centos-release | grep -q '\(CentOS\)' && BASE="centos" VERSION=`grep -v '^#' /etc/centos-release | grep -Po '(?<=release )\d+'` elif [ -f /etc/redhat-release ]; then grep -v '^#' /etc/redhat-release | grep -q '\(Red Hat\)' && BASE="res" VERSION=`grep -v '^#' /etc/redhat-release | grep -Po '(?<=release )\d+'` elif [ -f /etc/openEuler-release ]; then grep -v '^#' /etc/openEuler-release | grep -q '\(openEuler\)' && BASE="openEuler" VERSION=`grep -v '^#' /etc/openEuler-release | grep -Po '(?<=release )(\d+\.)+\d+'` elif [ -f /etc/os-release ]; then BASE=$(source /etc/os-release; echo $ID) VERSION=$(source /etc/os-release; echo $VERSION_ID) fi Y_CLIENT_CODE_BASE="${{BASE:-unknown}}" Y_CLIENT_CODE_VERSION="${{VERSION:-unknown}}" }} function getY_MISSING() {{ local NEEDED="{PKG_NAME_YUM}" if [ $VENV_ENABLED -eq 1 ]; then NEEDED="{PKG_NAME_VENV}" fi Y_MISSING="" for P in $NEEDED; do rpm -q "$P" || Y_MISSING="$Y_MISSING $P" done }} echo "* check for necessary packages being installed..." getY_CLIENT_CODE_BASE echo "* client codebase is ${{Y_CLIENT_CODE_BASE}}-${{Y_CLIENT_CODE_VERSION}}" CLIENT_REPOS_ROOT="${{CLIENT_REPOS_ROOT:-https://${{HOSTNAME}}/pub/repositories}}" CLIENT_REPO_URL="${{CLIENT_REPOS_ROOT}}/${{Y_CLIENT_CODE_BASE}}/${{Y_CLIENT_CODE_VERSION}}/bootstrap" CLIENT_REPO_NAME="susemanager:bootstrap" CLIENT_REPO_FILE="/etc/yum.repos.d/$CLIENT_REPO_NAME.repo" # In case of Red Hat derivatives, check if bootstrap repository is available, if not, fallback to RES. if [ "$Y_CLIENT_CODE_BASE" == almalinux ] || \ [ "$Y_CLIENT_CODE_BASE" == rockylinux ] || \ [ "$Y_CLIENT_CODE_BASE" == oracle ] || \ [ "$Y_CLIENT_CODE_BASE" == alibaba ] || \ [ "$Y_CLIENT_CODE_BASE" == openEuler ] || \ [ "$Y_CLIENT_CODE_BASE" == centos ] ; then $FETCH $CLIENT_REPO_URL/repodata/repomd.xml &> /dev/null if [ $? -ne 0 ]; then echo "${{Y_CLIENT_CODE_BASE}} ${{Y_CLIENT_CODE_VERSION}} bootstrap repository not found, using RES${{Y_CLIENT_CODE_VERSION}} bootstrap repository instead" CLIENT_REPO_URL="${{CLIENT_REPOS_ROOT}}/res/${{Y_CLIENT_CODE_VERSION}}/bootstrap" fi fi setup_bootstrap_repo {TEST_VENV_CALL} getY_MISSING if [ -z "$Y_MISSING" ]; then echo " no packages missing." else echo "* going to install missing packages..." yum -y install $Y_MISSING for P in $Y_MISSING; do rpm -q "$P" || {{ echo "ERROR: Failed to install all missing packages." exit 1 }} done fi # try update main packages for registration from any repo which is available if [ $VENV_ENABLED -eq 1 ]; then yum -y upgrade {PKG_NAME_VENV_UPDATE_YUM} ||: else yum -y upgrade {PKG_NAME_UPDATE_YUM} $RHNLIB_PKG ||: fi elif [ "$INSTALLER" == zypper ]; then function getZ_CLIENT_CODE_BASE() {{ local BASE="" local VERSION="" local PATCHLEVEL="" if [ -r /etc/SuSE-release ]; then grep -q 'Enterprise' /etc/SuSE-release && BASE='sle' eval $(grep '^\(VERSION\|PATCHLEVEL\)' /etc/SuSE-release | tr -d '[:blank:]') if [ "$BASE" != "sle" ]; then grep -q 'openSUSE' /etc/SuSE-release && BASE='opensuse' VERSION="$(grep '^\(VERSION\)' /etc/SuSE-release | tr -d '[:blank:]' | sed -n 's/.*=\([[:digit:]]\+\).*/\\1/p')" PATCHLEVEL="$(grep '^\(VERSION\)' /etc/SuSE-release | tr -d '[:blank:]' | sed -n 's/.*\.\([[:digit:]]*\).*/\\1/p')" fi elif [ -r /etc/os-release ]; then grep -q 'Enterprise' /etc/os-release && BASE='sle' if [ "$BASE" != "sle" ]; then grep -q 'openSUSE' /etc/os-release && BASE='opensuse' fi grep -q 'Micro' /etc/os-release && BASE="${{BASE}}micro" VERSION="$(grep '^\(VERSION_ID\)' /etc/os-release | sed -n 's/.*"\([[:digit:]]\+\).*/\\1/p')" PATCHLEVEL="$(grep '^\(VERSION_ID\)' /etc/os-release | sed -n 's/.*\.\([[:digit:]]*\).*/\\1/p')" # openSUSE MicroOS grep -q 'MicroOS' /etc/os-release && BASE='opensusemicroos' && VERSION='latest' # openSUSE Tumbleweed grep -q 'Tumbleweed' /etc/os-release && BASE='opensusetumbleweed' && VERSION='latest' fi Z_CLIENT_CODE_BASE="${{BASE:-unknown}}" Z_CLIENT_CODE_VERSION="${{VERSION:-unknown}}" Z_CLIENT_CODE_PATCHLEVEL="${{PATCHLEVEL:-0}}" }} function getZ_MISSING() {{ local NEEDED="{PKG_NAME}" if [ $VENV_ENABLED -eq 1 ]; then NEEDED="{PKG_NAME_VENV}" fi if [ "$Z_CLIENT_CODE_BASE" == "sle" -a "$Z_CLIENT_CODE_VERSION" == "10" ]; then # (bnc#789373) Code 10 product migration requires 'xsltproc' being installed which 'xsltproc' || NEEDED="$NEEDED libxslt" fi Z_MISSING="" for P in $NEEDED; do rpm -q "$P" || Z_MISSING="$Z_MISSING $P" done }} echo "* check for necessary packages being installed..." # client codebase determines repo url to use and whether additional # preparations are needed before installing the missing packages. getZ_CLIENT_CODE_BASE echo "* client codebase is ${{Z_CLIENT_CODE_BASE}}-${{Z_CLIENT_CODE_VERSION}}-sp${{Z_CLIENT_CODE_PATCHLEVEL}}" CLIENT_REPOS_ROOT="${{CLIENT_REPOS_ROOT:-${{HTTPS_PUB_DIRECTORY}}/repositories}}" CLIENT_REPO_URL="${{CLIENT_REPOS_ROOT}}/${{Z_CLIENT_CODE_BASE}}/${{Z_CLIENT_CODE_VERSION}}/${{Z_CLIENT_CODE_PATCHLEVEL}}/bootstrap" CLIENT_REPO_NAME="susemanager:bootstrap" CLIENT_REPO_FILE="/etc/zypp/repos.d/$CLIENT_REPO_NAME.repo" {TEST_VENV_CALL} getZ_MISSING if [ -z "$Z_MISSING" ]; then echo " no packages missing." setup_bootstrap_repo else echo "* going to install missing packages..." # Note: We try to install the missing packages even if adding the repo fails. # Might be some other system repo provides them instead. setup_bootstrap_repo if [ -z "$SNAPSHOT_ID" ]; then zypper --non-interactive --gpg-auto-import-keys refresh "$CLIENT_REPO_NAME" # install missing packages zypper --non-interactive in $Z_MISSING for P in $Z_MISSING; do rpm -q --whatprovides "$P" || {{ echo "ERROR: Failed to install all missing packages." exit 1 }} done else call_tukit "zypper --non-interactive --gpg-auto-import-keys refresh '$CLIENT_REPO_NAME'" if ! call_tukit "zypper --non-interactive install $Z_MISSING"; then echo "ERROR: Failed to install all required packages." tukit abort "$SNAPSHOT_ID" exit 1 fi fi fi # try update main packages for registration from any repo which is available if [ $VENV_ENABLED -eq 1 ]; then if [ -z "$SNAPSHOT_ID" ]; then zypper --non-interactive up {PKG_NAME_VENV_UPDATE} ||: else call_tukit "zypper --non-interactive update {PKG_NAME_VENV_UPDATE} ||:" fi else if [ -z "$SNAPSHOT_ID" ]; then zypper --non-interactive up {PKG_NAME_UPDATE} $RHNLIB_PKG ||: else call_tukit "zypper --non-interactive update {PKG_NAME_UPDATE} $RHNLIB_PKG ||:" fi fi elif [ "$INSTALLER" == apt ]; then function check_deb_pkg_installed {{ dpkg-query -W -f='${{Status}}' $1 2>/dev/null | grep -q "ok installed" }} function getA_CLIENT_CODE_BASE() {{ local BASE="" local VERSION="" local VARIANT_ID="" if [ -f /etc/os-release ]; then BASE=$(source /etc/os-release; echo $ID) VERSION=$(source /etc/os-release; echo $VERSION_ID) VARIANT_ID=$(source /etc/os-release; echo $VARIANT_ID) fi A_CLIENT_CODE_BASE="${{BASE:-unknown}}" local VERCOMPS=(${{VERSION/\./ }}) # split into an array 18.04 -> (18 04) A_CLIENT_CODE_MAJOR_VERSION=${{VERCOMPS[0]}} # Ubuntu only if [ "${{BASE}}" == "ubuntu" ]; then A_CLIENT_CODE_MINOR_VERSION=$((${{VERCOMPS[1]}} + 0)) # convert "04" -> 4 fi A_CLIENT_VARIANT_ID="${{VARIANT_ID:-unknown}}" }} function getA_MISSING() {{ local NEEDED="salt-common salt-minion" if [ $VENV_ENABLED -eq 1 ]; then NEEDED="venv-salt-minion" elif [[ $A_CLIENT_CODE_BASE == "ubuntu" && $A_CLIENT_CODE_MAJOR_VERSION == 18 ]]; then # Ubuntu 18.04 needs these extra dependencies. They are not specified in # python3-salt because we don't maintain multiple .deb build instructions # and we can't add logic that adds the deps depending on which OS the .deb # is built for. NEEDED="$NEEDED python3-contextvars python3-immutables" fi A_MISSING="" for P in $NEEDED; do check_deb_pkg_installed "$P" || A_MISSING="$A_MISSING $P" done }} function test_deb_repo_exists() {{ local repourl="$CLIENT_REPO_URL" $FETCH $repourl/dists/bootstrap/Release if [ ! -f "Release" ]; then echo "Bootstrap repo '$repourl' does not exist." repourl="" CLIENT_REPO_URL="" fi rm -f Release }} function setup_deb_bootstrap_repo() {{ local repopath="$CLIENT_REPO_FILE" local repourl="$CLIENT_REPO_URL" test_deb_repo_exists if [ -n "$CLIENT_REPO_URL" ]; then echo " adding client software repository at $repourl" echo "deb [trusted=yes] $repourl bootstrap main" >"$repopath" fi }} echo "* check for necessary packages being installed..." getA_CLIENT_CODE_BASE if [ "${{A_CLIENT_CODE_BASE}}" == "astra" ]; then echo "* client codebase is ${{A_CLIENT_CODE_BASE}}-${{A_CLIENT_VARIANT_ID}}" else echo "* client codebase is ${{A_CLIENT_CODE_BASE}}-${{A_CLIENT_CODE_MAJOR_VERSION}}.${{A_CLIENT_CODE_MINOR_VERSION}}" fi CLIENT_REPOS_ROOT="${{CLIENT_REPOS_ROOT:-${{HTTPS_PUB_DIRECTORY}}/repositories}}" # Debian does not need minor version in the bootstrap repo URL if [ "${{A_CLIENT_CODE_BASE}}" == "debian" ] || [ "${{A_CLIENT_CODE_BASE}}" == "raspbian" ]; then CLIENT_REPO_URL="${{CLIENT_REPOS_ROOT}}/${{A_CLIENT_CODE_BASE}}/${{A_CLIENT_CODE_MAJOR_VERSION}}/bootstrap" elif [ "${{A_CLIENT_CODE_BASE}}" == "astra" ]; then CLIENT_REPO_URL="${{CLIENT_REPOS_ROOT}}/${{A_CLIENT_CODE_BASE}}/${{A_CLIENT_VARIANT_ID}}/bootstrap" else CLIENT_REPO_URL="${{CLIENT_REPOS_ROOT}}/${{A_CLIENT_CODE_BASE}}/${{A_CLIENT_CODE_MAJOR_VERSION}}/${{A_CLIENT_CODE_MINOR_VERSION}}/bootstrap" fi CLIENT_REPO_NAME="susemanager_bootstrap" CLIENT_REPO_FILE="/etc/apt/sources.list.d/$CLIENT_REPO_NAME.list" setup_deb_bootstrap_repo {TEST_VENV_CALL} getA_MISSING apt-get --yes update if [ -z "$A_MISSING" ]; then echo " no packages missing." else echo "* going to install missing packages..." # check if there are any leftovers from previous salt-minion installs and purge them SALT_MINION_PKG="salt-minion" if [ $VENV_ENABLED -eq 1 ]; then SALT_MINION_PKG="venv-salt-minion" fi dpkg-query -W -f='${{Status}}' "$SALT_MINION_PKG" 2>/dev/null | grep -q "deinstall ok config-files" if [ "$?" -eq 0 ]; then echo "* purging previous Salt config files" apt-get --yes purge "$SALT_MINION_PKG" if [ $VENV_ENABLED -eq 1 ]; then rm -rf /etc/venv-salt-minion/ else apt-get purge salt-common rm -rf /etc/salt/minion.d/ fi fi apt-get --yes install --no-install-recommends $A_MISSING for P in $A_MISSING; do check_deb_pkg_installed "$P" || {{ echo "ERROR: Failed to install all missing packages." exit 1 }} done fi # try update main packages for registration from any repo which is available if [ $VENV_ENABLED -eq 1 ]; then apt-get --yes install --no-install-recommends --only-upgrade venv-salt-minion ||: else apt-get --yes install --no-install-recommends --only-upgrade salt-common salt-minion ||: fi # remove bootstrap repo rm -f $CLIENT_REPO_FILE fi remove_bootstrap_repo """.format( PKG_NAME=" ".join(PKG_NAME), PKG_NAME_YUM=" ".join(PKG_NAME_YUM), PKG_NAME_UPDATE=" ".join(PKG_NAME_UPDATE), PKG_NAME_UPDATE_YUM=" ".join(PKG_NAME_UPDATE_YUM), PKG_NAME_VENV=" ".join(PKG_NAME_VENV), PKG_NAME_VENV_UPDATE=" ".join(PKG_NAME_VENV_UPDATE), PKG_NAME_VENV_UPDATE_YUM=" ".join(PKG_NAME_VENV_UPDATE_YUM), TEST_VENV_FUNC=TEST_VENV_FUNC, TEST_VENV_CALL=TEST_VENV_CALL, ) # pylint: disable-next=invalid-name def getGPGKeyImportSh(): return """\ echo echo "PREPARE GPG KEYS AND CORPORATE PUBLIC CA CERT" echo "-------------------------------------------------" if [ ! -z "$ORG_GPG_KEY" ]; then echo echo "* importing organizational GPG keys" for GPG_KEY in $(echo "$ORG_GPG_KEY" | tr "," " "); do rm -f ${GPG_KEY} $FETCH ${HTTPS_PUB_DIRECTORY}/${GPG_KEY} if [ "$INSTALLER" == "apt" ]; then apt-get --yes install --no-install-recommends gnupg apt-key add $GPG_KEY else rpm --import $GPG_KEY fi rm -f ${GPG_KEY} done else echo "* no organizational GPG keys to import" fi """ # pylint: disable-next=invalid-name def getCorpCACertSh(): return """\ echo if [ "$INSTALLER" == "apt" ]; then CERT_DIR=/usr/local/share/ca-certificates/susemanager TRUST_DIR=/usr/local/share/ca-certificates/susemanager UPDATE_TRUST_CMD="/usr/sbin/update-ca-certificates" ORG_CA_CERT=RHN-ORG-TRUSTED-SSL-CERT else CERT_DIR=/usr/share/rhn TRUST_DIR=/etc/pki/ca-trust/source/anchors UPDATE_TRUST_CMD="/usr/bin/update-ca-trust extract" fi if [ "$INSTALLER" == "apt" ]; then CERT_FILE="${ORG_CA_CERT}.crt" else CERT_FILE=${ORG_CA_CERT} fi function updateCertificates() { if [ -d /etc/pki/ca-trust/source/anchors -a -x /usr/bin/update-ca-trust ]; then TRUST_DIR=/etc/pki/ca-trust/source/anchors elif [ -d /etc/pki/trust/anchors/ -a -x /usr/sbin/update-ca-certificates ]; then # SLE 12 TRUST_DIR=/etc/pki/trust/anchors UPDATE_TRUST_CMD="/usr/sbin/update-ca-certificates" elif [ -d /etc/ssl/certs -a -x /usr/bin/c_rehash -a "$INSTALLER" == "zypper" ]; then # SLE 11 TRUST_DIR=/etc/ssl/certs UPDATE_TRUST_CMD="/usr/bin/c_rehash" rm -f $TRUST_DIR/RHN-ORG-TRUSTED-SSL-CERT.pem rm -f $TRUST_DIR/RHN-ORG-TRUSTED-SSL-CERT-*.pem if [ -f $CERT_DIR/$CERT_FILE ]; then ln -sf $CERT_DIR/$CERT_FILE $TRUST_DIR/RHN-ORG-TRUSTED-SSL-CERT.pem if [ $(grep -- "-----BEGIN CERTIFICATE-----" $CERT_DIR/$CERT_FILE | wc -l) -gt 1 ]; then csplit -b "%02d.pem" -f $TRUST_DIR/RHN-ORG-TRUSTED-SSL-CERT- $CERT_DIR/$CERT_FILE '/-----BEGIN CERTIFICATE-----/' '{*}' fi fi $UPDATE_TRUST_CMD >/dev/null return fi if [ ! -d $TRUST_DIR ]; then return fi if [ "$CERT_DIR" != "$TRUST_DIR" ]; then if [ -z "$SNAPSHOT_ID" ]; then if [ -f $CERT_DIR/$CERT_FILE ]; then ln -sf $CERT_DIR/$CERT_FILE $TRUST_DIR else rm -f $TRUST_DIR/$CERT_FILE fi else if call_tukit "test -f '$CERT_DIR/$CERT_FILE'"; then call_tukit "ln -sf '$CERT_DIR/$CERT_FILE' '$TRUST_DIR'" else call_tukit "rm -f '$TRUST_DIR/$CERT_FILE'" fi fi fi $UPDATE_TRUST_CMD } echo "* attempting to install corporate public CA cert" ### Check for Dynamic CA-Trust Updates - applies to RedHat and SLE-ES systems ### if [ -x /usr/bin/update-ca-trust ]; then if [ "$(/usr/bin/update-ca-trust check | grep 'PEM/JAVA Status: DISABLED')" != "" ]; then echo "ERROR: Dynamic CA-Trust > Updates are disabled. Enable Dynamic CA-Trust Updates with '/usr/bin/update-ca-trust force-enable'" echo "Finally, restart the onboarding sequence." exit 1 fi fi rm -f ${ORG_CA_CERT} $FETCH ${HTTPS_PUB_DIRECTORY}/${ORG_CA_CERT} if [ -n "$SNAPSHOT_ID" ]; then # we need to copy certificate to the trustroot outside of transaction for zypper cp "$ORG_CA_CERT" /etc/pki/trust/anchors/ call_tukit "test -d '$CERT_DIR' || mkdir -p '$CERT_DIR'" call_tukit "cp '/etc/pki/trust/anchors/$ORG_CA_CERT' '${CERT_DIR}/${CERT_FILE}'" else test -d "$CERT_DIR" || mkdir -p "$CERT_DIR" mv "$ORG_CA_CERT" "${CERT_DIR}/${CERT_FILE}" fi echo "* update certificates" updateCertificates """ # pylint: disable-next=invalid-name def getRegistrationSaltSh(productName): # pylint: disable-next=consider-using-f-string return """\ echo echo "REGISTRATION" echo "------------" # Should have created an activation key or keys on the {productName}'s # website and edited the value of ACTIVATION_KEYS above. # # If you require use of several different activation keys, copy this file and # change the string as needed. # if [[ $ACTIVATION_KEYS =~ , ]]; then echo "*** ERROR: Multiple activation keys are not supported with salt!" exit 1 fi SNAPSHOT_PREFIX="" if [ -n "$SNAPSHOT_ID" ]; then SNAPSHOT_PREFIX="/var/lib/overlay/$SNAPSHOT_ID" fi MINION_ID_FILE="${{SNAPSHOT_PREFIX}}/etc/salt/minion_id" MINION_PKI_CONF="${{SNAPSHOT_PREFIX}}/etc/salt/pki" MINION_CONFIG_DIR="${{SNAPSHOT_PREFIX}}/etc/salt/minion.d" SUSEMANAGER_MASTER_FILE="${{MINION_CONFIG_DIR}}/susemanager.conf" MINION_SERVICE="salt-minion" if [ $VENV_ENABLED -eq 1 ]; then MINION_ID_FILE="${{SNAPSHOT_PREFIX}}/etc/venv-salt-minion/minion_id" MINION_PKI_CONF="${{SNAPSHOT_PREFIX}}/etc/venv-salt-minion/pki" MINION_CONFIG_DIR="${{SNAPSHOT_PREFIX}}/etc/venv-salt-minion/minion.d" SUSEMANAGER_MASTER_FILE="${{MINION_CONFIG_DIR}}/susemanager.conf" MINION_SERVICE="venv-salt-minion" fi if [ $REGISTER_THIS_BOX -eq 1 ]; then echo "* registering" echo "$MYNAME" > "$MINION_ID_FILE" cat <<EOF > "$SUSEMANAGER_MASTER_FILE" master: $HOSTNAME server_id_use_crc: adler32 enable_legacy_startup_events: False enable_fqdns_grains: False start_event_grains: [machine_id, saltboot_initrd, susemanager] mine_enabled: False EOF cat <<EOF >> "$SUSEMANAGER_MASTER_FILE" grains: susemanager: EOF if [ -n "$ACTIVATION_KEYS" ]; then echo "Using activation key: \"$ACTIVATION_KEYS\"" cat <<EOF >>"$SUSEMANAGER_MASTER_FILE" activation_key: "$(echo $ACTIVATION_KEYS | cut -d, -f1)" EOF fi if [ -n "$REACTIVATION_KEY" ]; then echo "Using reactivation key: \"$REACTIVATION_KEY\"" cat <<EOF >>"$SUSEMANAGER_MASTER_FILE" management_key: "$(echo $REACTIVATION_KEY)" EOF fi if [ -n "$PROFILE_NAME" ]; then echo "Setting profile name to: $PROFILE_NAME" cat <<EOF >>"$SUSEMANAGER_MASTER_FILE" profile_name: "$(echo $PROFILE_NAME)" EOF fi cat <<EOF >> "$SUSEMANAGER_MASTER_FILE" system-environment: modules: pkg: _: SALT_RUNNING: 1 EOF # Remove old minion keys so reregistration do different master works if [ -d "$MINION_PKI_CONF" ]; then rm -r "$MINION_PKI_CONF" fi if [ -n "$SNAPSHOT_ID" ]; then cat <<EOF >> "${{MINION_CONFIG_DIR}}/transactional_update.conf" # Enable the transactional_update executor module_executors: - transactional_update - direct_call # Include beacon to check for pending transactions indicating that a reboot is necessary beacons: reboot_info: - interval: 10 EOF if ! test -f /etc/transactional-update.conf; then cp /usr/etc/transactional-update.conf /etc/transactional-update.conf fi . /etc/transactional-update.conf if [ -z "$REBOOT_METHOD" ] || [ "$REBOOT_METHOD" = "auto" ]; then sed -i '/^REBOOT_METHOD=/d' /etc/transactional-update.conf echo "REBOOT_METHOD=systemd" >> /etc/transactional-update.conf fi fi # -n SNAPSHOT_ID fi # REGISTER_THIS_BOX eq 1 echo "* removing TLS certificate used for bootstrap" echo " (will be re-added via salt state)" removeTLSCertificate echo "* starting salt daemon and enabling it during boot" if [ -n "$SNAPSHOT_ID" ]; then call_tukit "systemctl enable '$MINION_SERVICE'" tukit -q close $SNAPSHOT_ID if [ "$SCHEDULE_REBOOT_AFTER_TRANSACTION" -eq 1 ]; then transactional-update reboot else echo "** Reboot system to apply changes" fi elif [ -f /usr/lib/systemd/system/$MINION_SERVICE.service ] || [ -f /lib/systemd/system/$MINION_SERVICE.service ]; then systemctl enable $MINION_SERVICE systemctl restart $MINION_SERVICE else /etc/init.d/$MINION_SERVICE restart /sbin/chkconfig --add $MINION_SERVICE fi echo "-bootstrap complete-" """.format( productName=productName ) # pylint: disable-next=invalid-name def removeTLSCertificate(): """ This method adds bash instructions to the bootstrap script to correctly remove TLS certificate used to install salt packages to bootstrap the minion. Since TLS certificates will be installed again with a Salt state during onboarding, this is required to avoid duplicates in TLS certificates. """ return """\ function removeTLSCertificate() { if [ "$INSTALLER" == "apt" ]; then CERT_DIR=/usr/local/share/ca-certificates/susemanager TRUST_DIR=/usr/local/share/ca-certificates/susemanager UPDATE_TRUST_CMD="/usr/sbin/update-ca-certificates" ORG_CA_CERT=RHN-ORG-TRUSTED-SSL-CERT else CERT_DIR=/usr/share/rhn TRUST_DIR=/etc/pki/ca-trust/source/anchors UPDATE_TRUST_CMD="/usr/bin/update-ca-trust extract" fi if [ -f /usr/share/rhn/${ORG_CA_CERT} ]; then CERT_FILE=${ORG_CA_CERT} rm -f /usr/share/rhn/${ORG_CA_CERT} elif [ -f /usr/local/share/ca-certificates/susemanager/${ORG_CA_CERT}.crt ]; then CERT_FILE=${ORG_CA_CERT}.crt rm -f /usr/local/share/ca-certificates/susemanager/${CERT_FILE} fi updateCertificates } """ 07070100000014000081ED000003E80000006400000001662798DF0000E348000000000000000000000000000000000000002600000000spacewalk-certs-tools/rhn_ssl_tool.py#!/usr/bin/python # pylint: disable=missing-module-docstring # # Copyright (c) 2008--2015 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # RHN SSL Maintenance Tool (main module) # # *NOTE* # This module is intended to be imported and not run directly though it can # be. At the time of this note, the excutable wrapping this module was # /usr/bin/rhn-ssl-tool. # # Generate and maintain SSL keys & certificates. One can also build RPMs in # the RHN product context. # # NOTE: this tool is geared for RHN product usage, but can be used outside of # that context to some degree. # # Author: Todd Warner <taw@redhat.com> # ## language imports from __future__ import print_function import copy import os import sys import glob import pwd import time import shutil import getpass ## local imports from .sslToolCli import ( processCommandline, CertExpTooShortException, CertExpTooLongException, InvalidCountryCodeException, ) from .sslToolLib import ( RhnSslToolException, gendir, chdir, getMachineName, fixSerial, TempDir, errnoGeneralError, errnoSuccess, ) from uyuni.common.fileutils import rotateFile, rhn_popen, cleanupAbsPath from uyuni.common.rhn_rpm import ( hdrLabelCompare, sortRPMs, get_package_header, getInstalledHeader, ) from .sslToolConfig import ( ConfigFile, figureSerial, getOption, CERT_PATH, DEFS, MD, CRYPTO, LEGACY_SERVER_RPM_NAME1, LEGACY_SERVER_RPM_NAME2, CA_OPENSSL_CNF_NAME, SERVER_OPENSSL_CNF_NAME, POST_UNINSTALL_SCRIPT, SERVER_RPM_SUMMARY, CA_CERT_RPM_SUMMARY, ) from rhn.stringutils import bstr, sstr class GenPrivateCaKeyException(RhnSslToolException): """private CA key generation error""" class GenPublicCaCertException(RhnSslToolException): """public CA cert generation error""" class GenServerKeyException(RhnSslToolException): """private server key generation error""" class GenServerCertReqException(RhnSslToolException): """server cert request generation error""" class GenServerCertException(RhnSslToolException): """server cert generation error""" class GenCaCertRpmException(RhnSslToolException): """CA public certificate RPM generation error""" class GenServerRpmException(RhnSslToolException): """server RPM generation error""" class GenServerTarException(RhnSslToolException): """server tar archive generation error""" class FailedFileDependencyException(Exception): """missing a file needed for this step""" # pylint: disable-next=invalid-name def dependencyCheck(filename): if not os.path.exists(filename): raise FailedFileDependencyException(filename) # pylint: disable-next=invalid-name def pathJoin(path, filename): filename = os.path.basename(filename) return os.path.join(path, filename) # pylint: disable-next=invalid-name def legacyTreeFixup(d): """move old server.* files to and "unknown" machinename directory Most of this is RHN Satellite 2.* and 3.* changes. Near the end we get to 3.6 changes. """ topdir = cleanupAbsPath(d["--dir"]) # pylint: disable-next=invalid-name oldTree = "/etc/sysconfig/rhn/ssl" if topdir != oldTree and os.path.exists(oldTree): sys.stderr.write( # pylint: disable-next=consider-using-f-string """\ WARNING: %s still exists even though %s is the currently configured build tree. You may wish to either (a) move %s to %s, or (b) point directly at the old tree by via the --dir option. """ % (oldTree, topdir, oldTree, topdir) ) sys.stderr.write("Pausing for 5 secs") for i in range(5): sys.stderr.write(".") time.sleep(1) sys.stderr.write("\n") unknown = os.path.join(topdir, "unknown") server_rpm_name = os.path.basename(d.get("--server-rpm", "")) # pylint: disable-next=invalid-name serverKeyPairDir = None if "--set-hostname" in d: # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) while os.path.exists(unknown): # to avoid clashing with a possible "unknown" machinename unknown = unknown + "_" old_server_splat = os.path.join(topdir, "server.") # pylint: disable-next=invalid-name moveMessage = "" for ext in ("key", "csr", "crt"): if os.path.exists(old_server_splat + ext): gendir(unknown) files = glob.glob(old_server_splat + ext + "*") moved = [] for f in files: # move the files to the "unknown" directory new_server_splat = os.path.join(unknown, os.path.basename(f)) if not os.path.exists(new_server_splat): shutil.copy2(f, new_server_splat) os.unlink(f) moved.append(f) # if files and verbosity: if moved: s = "server." + ext + "*" # pylint: disable-next=invalid-name moveMessage = moveMessage + ( # pylint: disable-next=consider-using-f-string " <BUILD_DIR>/%s --> <BUILD_DIR>/%s/%s\n" % (s, os.path.basename(unknown), s) ) # move legacy server SSL RPMs. But if server_rpm_name is the same name # as the target RPM name, then we move the RPMs into the appropriate # machine name directory. for name in [LEGACY_SERVER_RPM_NAME1, LEGACY_SERVER_RPM_NAME2]: old_server_rpms = glob.glob(os.path.join(topdir, name + "-*-*.*.rpm")) # pylint: disable-next=invalid-name movedYN = 0 for old_rpm in old_server_rpms: # pylint: disable-next=invalid-name targetDir = unknown old_hdr = get_package_header(old_rpm) if old_hdr and old_hdr["name"] == server_rpm_name and serverKeyPairDir: # pylint: disable-next=invalid-name targetDir = serverKeyPairDir gendir(targetDir) # move the files to the targetDir directory new_rpm = os.path.join(targetDir, os.path.basename(old_rpm)) if not os.path.exists(new_rpm): shutil.copy2(old_rpm, new_rpm) os.unlink(old_rpm) # pylint: disable-next=invalid-name movedYN = 1 if movedYN: s = name + "-*-*.{noarch,src}.rpm" # pylint: disable-next=invalid-name moveMessage = ( moveMessage # pylint: disable-next=consider-using-f-string + """\ <BUILD_DIR>/%s --> <BUILD_DIR>/%s/%s\n""" % (s, os.path.basename(targetDir), s) ) # I move the first 100 .pem files I find # if there is more than that... oh well # pylint: disable-next=invalid-name movedYN = 0 for i in range(100): serial = fixSerial(hex(i)) # pylint: disable-next=invalid-name oldPemPath = os.path.join(topdir, serial + ".pem") # pylint: disable-next=invalid-name newPemPath = os.path.join(unknown, serial + ".pem") if os.path.exists(oldPemPath) and not os.path.exists(newPemPath): gendir(unknown) shutil.copy2(oldPemPath, newPemPath) os.unlink(oldPemPath) # pylint: disable-next=invalid-name movedYN = 1 if movedYN: # pylint: disable-next=invalid-name moveMessage = moveMessage + ( # pylint: disable-next=consider-using-f-string " <BUILD_DIR>/HEX*.pem --> <BUILD_DIR>/%s/HEX*.pem\n" % os.path.basename(unknown) ) if moveMessage: # pylint: disable-next=consider-using-f-string sys.stdout.write("\nLegacy tree structured file(s) moved:\n%s" % moveMessage) # move rhn-org-httpd-ssl-MACHINENAME-VERSION.*.rpm files to the # MACHINENAME directory! (an RHN 3.6.0 change) # pylint: disable-next=invalid-name rootFilename = pathJoin(topdir, "rhn-org-httpd-ssl-key-pair-") filenames = glob.glob(rootFilename + "*") for filename in filenames: # note: assuming version-rel is of that form. machinename = filename[len(rootFilename) :] machinename = "-".join(machinename.split("-")[:-2]) # pylint: disable-next=invalid-name serverKeySetDir = pathJoin(topdir, machinename) gendir(serverKeySetDir) fileto = pathJoin(serverKeySetDir, filename) if os.path.exists(fileto): rotateFile(filepath=fileto, verbosity=0) shutil.copy2(filename, fileto) os.unlink(filename) print( # pylint: disable-next=consider-using-f-string """\ Moved (legacy tree cleanup): %s ...moved to... %s""" % (filename, fileto) ) # pylint: disable-next=invalid-name _workDirObj = None # pylint: disable-next=invalid-name def _getWorkDir(): global _workDirObj if not _workDirObj: _workDirObj = TempDir() return _workDirObj.getdir() # pylint: disable-next=invalid-name def getCAPassword(options, confirmYN=1): # pylint: disable-next=global-variable-not-assigned global DEFS while not options.password: # pylint: disable-next=invalid-name pw = _pw = None if options.password_file: if os.path.isfile(options.password_file): # pylint: disable-next=unspecified-encoding with open(options.password_file, "r") as fd: # pylint: disable-next=invalid-name pw = _pw = fd.read().strip() else: # pylint: disable-next=consider-using-f-string print("No such file '{}'".format(options.password_file)) while not pw: pw = getpass.getpass("CA password: ") if confirmYN: while not _pw: # pylint: disable-next=invalid-name _pw = getpass.getpass("CA password confirmation: ") if pw != _pw: print("Passwords do not match.\n") pw = None DEFS["--password"] = options.password = pw return options.password # pylint: disable-next=invalid-name def genPrivateCaKey(password, d, verbosity=0, forceYN=0): """private CA key generation""" gendir(d["--dir"]) ca_key = os.path.join(d["--dir"], os.path.basename(d["--ca-key"])) if not forceYN and os.path.exists(ca_key): sys.stderr.write( # pylint: disable-next=consider-using-f-string """\ ERROR: a CA private key already exists: %s If you wish to generate a new one, use the --force option. """ % ca_key ) sys.exit(errnoGeneralError) args = ( # pylint: disable-next=consider-using-f-string "/usr/bin/openssl genpkey -pass pass:%s %s -out %s -algorithm rsa -pkeyopt rsa_keygen_bits:4096" % ("%s", CRYPTO, repr(cleanupAbsPath(ca_key))) ) if verbosity >= 0: # pylint: disable-next=consider-using-f-string print("Generating private CA key: %s" % ca_key) if verbosity > 1: print("Commandline:", args % "PASSWORD") try: rotated = rotateFile(filepath=ca_key, verbosity=verbosity) if verbosity >= 0 and rotated: # pylint: disable-next=consider-using-f-string print("Rotated: %s --> %s" % (d["--ca-key"], os.path.basename(rotated))) except ValueError: pass cwd = chdir(_getWorkDir()) try: ret, out_stream, err_stream = rhn_popen(args % repr(password)) finally: chdir(cwd) out = out_stream.read() out_stream.close() err = err_stream.read() err_stream.close() if ret: raise GenPrivateCaKeyException( # pylint: disable-next=consider-using-f-string "Certificate Authority private SSL " "key generation failed:\n%s\n%s" % (out, err) ) if verbosity > 2: if out: print("STDOUT:", out) if err: print("STDERR:", err) # permissions: os.chmod(ca_key, int("0600", 8)) # pylint: disable-next=invalid-name def genPublicCaCert_dependencies(password, d, forceYN=0): """public CA certificate (client-side) generation""" gendir(d["--dir"]) ca_key = os.path.join(d["--dir"], os.path.basename(d["--ca-key"])) ca_cert = os.path.join(d["--dir"], os.path.basename(d["--ca-cert"])) if not forceYN and os.path.exists(ca_cert): sys.stderr.write( # pylint: disable-next=consider-using-f-string """\ ERROR: a CA public certificate already exists: %s If you wish to generate a new one, use the --force option. """ % ca_cert ) sys.exit(errnoGeneralError) dependencyCheck(ca_key) if password is None: sys.stderr.write("ERROR: a CA password must be supplied.\n") sys.exit(errnoGeneralError) # pylint: disable-next=invalid-name def genCaConf(d, verbosity=0): """generate the openssl ca config""" ca_openssl_cnf = os.path.join(d["--dir"], CA_OPENSSL_CNF_NAME) # pylint: disable-next=invalid-name configFile = ConfigFile(ca_openssl_cnf) data = copy.deepcopy(d) if "--set-hostname" in data: del data["--set-hostname"] configFile.save(data, caYN=1, verbosity=verbosity) return configFile # pylint: disable-next=invalid-name def genPublicCaCert(password, d, verbosity=0, forceYN=0): """public CA certificate (client-side) generation""" ca_key = os.path.join(d["--dir"], os.path.basename(d["--ca-key"])) ca_cert_name = os.path.basename(d["--ca-cert"]) ca_cert = os.path.join(d["--dir"], ca_cert_name) genPublicCaCert_dependencies(password, d, forceYN) # pylint: disable-next=invalid-name configFile = genCaConf(d, verbosity) args = ( # pylint: disable-next=consider-using-f-string "/usr/bin/openssl req -passin pass:%s -text -config %s " "-new -x509 -days %s -%s -key %s -out %s" % ( "%s", repr(cleanupAbsPath(configFile.filename)), repr(d["--cert-expiration"]), MD, repr(cleanupAbsPath(ca_key)), repr(cleanupAbsPath(ca_cert)), ) ) if verbosity >= 0: # pylint: disable-next=consider-using-f-string print("\nGenerating public CA certificate: %s" % ca_cert) print("Using distinguishing variables:") for k in ( "--set-country", "--set-state", "--set-city", "--set-org", "--set-org-unit", "--set-common-name", "--set-email", ): # pylint: disable-next=consider-using-f-string print(' %s%s = "%s"' % (k, " " * (18 - len(k)), d[k])) if verbosity > 1: print("Commandline:", args % "PASSWORD") try: rotated = rotateFile(filepath=ca_cert, verbosity=verbosity) if verbosity >= 0 and rotated: # pylint: disable-next=consider-using-f-string print("Rotated: %s --> %s" % (d["--ca-cert"], os.path.basename(rotated))) except ValueError: pass cwd = chdir(_getWorkDir()) try: ret, out_stream, err_stream = rhn_popen(args % repr(password)) finally: chdir(cwd) out = out_stream.read() out_stream.close() err = err_stream.read() err_stream.close() if ret: raise GenPublicCaCertException( # pylint: disable-next=consider-using-f-string "Certificate Authority public " "SSL certificate generation failed:\n%s\n" "%s" % (out, err) ) if verbosity > 2: if out: print("STDOUT:", out) if err: print("STDERR:", err) latest_txt = os.path.join(d["--dir"], "latest.txt") fo = open(latest_txt, "wb") # pylint: disable-next=consider-using-f-string fo.write(bstr("%s\n" % ca_cert_name)) fo.close() # permissions: os.chmod(ca_cert, int("0644", 8)) os.chmod(latest_txt, int("0644", 8)) # pylint: disable-next=invalid-name def genServerKey(d, verbosity=0): """private server key generation""" # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) gendir(serverKeyPairDir) server_key = os.path.join(serverKeyPairDir, os.path.basename(d["--server-key"])) # pylint: disable-next=consider-using-f-string args = "/usr/bin/openssl genrsa -out %s 4096" % (repr(cleanupAbsPath(server_key))) # generate the server key if verbosity >= 0: # pylint: disable-next=consider-using-f-string print("\nGenerating the web server's SSL private key: %s" % server_key) if verbosity > 1: print("Commandline:", args) try: rotated = rotateFile(filepath=server_key, verbosity=verbosity) if verbosity >= 0 and rotated: # pylint: disable-next=consider-using-f-string print("Rotated: %s --> %s" % (d["--server-key"], os.path.basename(rotated))) except ValueError: pass cwd = chdir(_getWorkDir()) try: ret, out_stream, err_stream = rhn_popen(args) finally: chdir(cwd) out = out_stream.read() out_stream.close() err = err_stream.read() err_stream.close() if ret: raise GenServerKeyException( # pylint: disable-next=consider-using-f-string "web server's SSL key generation failed:\n%s\n%s" % (out, err) ) if verbosity > 2: if out: print("STDOUT:", out) if err: print("STDERR:", err) # permissions: os.chmod(server_key, int("0600", 8)) # pylint: disable-next=invalid-name def genServerCertReq_dependencies(d): """private server cert request generation""" # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) gendir(serverKeyPairDir) server_key = os.path.join(serverKeyPairDir, os.path.basename(d["--server-key"])) dependencyCheck(server_key) # pylint: disable-next=invalid-name def genServerCertReq(d, verbosity=0): """private server cert request generation""" # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) server_key = os.path.join(serverKeyPairDir, os.path.basename(d["--server-key"])) server_cert_req = os.path.join( serverKeyPairDir, os.path.basename(d["--server-cert-req"]) ) server_openssl_cnf = os.path.join(serverKeyPairDir, SERVER_OPENSSL_CNF_NAME) genServerCertReq_dependencies(d) # XXX: hmm.. should private_key, etc. be set for this before the write? # either that you pull the key/certs from the files all together? # pylint: disable-next=invalid-name configFile = ConfigFile(server_openssl_cnf) if "--set-common-name" in d: del d["--set-common-name"] configFile.save(d, caYN=0, verbosity=verbosity) ## generate the server cert request # pylint: disable-next=consider-using-f-string args = "/usr/bin/openssl req -%s -text -config %s -new -key %s -out %s " % ( MD, repr(cleanupAbsPath(configFile.filename)), repr(cleanupAbsPath(server_key)), repr(cleanupAbsPath(server_cert_req)), ) if verbosity >= 0: # pylint: disable-next=consider-using-f-string print("\nGenerating web server's SSL certificate request: %s" % server_cert_req) print("Using distinguished names:") for k in ( "--set-country", "--set-state", "--set-city", "--set-org", "--set-org-unit", "--set-hostname", "--set-email", ): # pylint: disable-next=consider-using-f-string print(' %s%s = "%s"' % (k, " " * (18 - len(k)), d[k])) if verbosity > 1: print("Commandline:", args) try: rotated = rotateFile(filepath=server_cert_req, verbosity=verbosity) if verbosity >= 0 and rotated: print( # pylint: disable-next=consider-using-f-string "Rotated: %s --> %s" % (d["--server-cert-req"], os.path.basename(rotated)) ) except ValueError: pass cwd = chdir(_getWorkDir()) try: ret, out_stream, err_stream = rhn_popen(args) finally: chdir(cwd) out = out_stream.read() out_stream.close() err = err_stream.read() err_stream.close() if ret: raise GenServerCertReqException( # pylint: disable-next=consider-using-f-string "web server's SSL certificate request generation " "failed:\n%s\n%s" % (out, err) ) if verbosity > 2: if out: print("STDOUT:", out) if err: print("STDERR:", err) # permissions: os.chmod(server_cert_req, int("0600", 8)) # pylint: disable-next=invalid-name def genServerCert_dependencies(password, d, verbosity=0): """server cert generation and signing dependency check""" if password is None: sys.stderr.write("ERROR: a CA password must be supplied.\n") sys.exit(errnoGeneralError) # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) gendir(serverKeyPairDir) ca_key = os.path.join(d["--dir"], os.path.basename(d["--ca-key"])) ca_cert = os.path.join(d["--dir"], os.path.basename(d["--ca-cert"])) server_cert_req = os.path.join( serverKeyPairDir, os.path.basename(d["--server-cert-req"]) ) ca_openssl_cnf = os.path.join(d["--dir"], CA_OPENSSL_CNF_NAME) try: dependencyCheck(ca_openssl_cnf) except FailedFileDependencyException: genCaConf(d, verbosity) dependencyCheck(ca_key) dependencyCheck(ca_cert) dependencyCheck(server_cert_req) # pylint: disable-next=invalid-name def genServerCert(password, d, verbosity=0): """server cert generation and signing""" # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) genServerCert_dependencies(password, d, verbosity) ca_key = os.path.join(d["--dir"], os.path.basename(d["--ca-key"])) ca_cert = os.path.join(d["--dir"], os.path.basename(d["--ca-cert"])) server_cert_req = os.path.join( serverKeyPairDir, os.path.basename(d["--server-cert-req"]) ) server_cert = os.path.join(serverKeyPairDir, os.path.basename(d["--server-cert"])) ca_openssl_cnf = os.path.join(d["--dir"], CA_OPENSSL_CNF_NAME) index_txt = os.path.join(d["--dir"], "index.txt") serial = os.path.join(d["--dir"], "serial") try: os.unlink(index_txt) # pylint: disable-next=bare-except except: pass # figure out the serial file and truncate the index.txt file. ser = figureSerial(ca_cert, serial, index_txt) # need to insure the directory declared in the ca_openssl.cnf # file is current: # pylint: disable-next=invalid-name configFile = ConfigFile(ca_openssl_cnf) configFile.updateDir() args = ( # pylint: disable-next=consider-using-f-string "/usr/bin/openssl ca -extensions req_server_x509_extensions -passin pass:%s -outdir ./ -config %s " "-in %s -batch -cert %s -keyfile %s -startdate %s -days %s " "-md %s -out %s" % ( "%s", repr(cleanupAbsPath(ca_openssl_cnf)), repr(cleanupAbsPath(server_cert_req)), repr(cleanupAbsPath(ca_cert)), repr(cleanupAbsPath(ca_key)), d["--startdate"], repr(d["--cert-expiration"]), MD, repr(cleanupAbsPath(server_cert)), ) ) if verbosity >= 0: print( # pylint: disable-next=consider-using-f-string "\nGenerating/signing web server's SSL certificate: %s" % d["--server-cert"] ) if verbosity > 1: print("Commandline:", args % "PASSWORD") try: rotated = rotateFile(filepath=server_cert, verbosity=verbosity) if verbosity >= 0 and rotated: print( # pylint: disable-next=consider-using-f-string "Rotated: %s --> %s" % (d["--server-cert"], os.path.basename(rotated)) ) except ValueError: pass cwd = chdir(_getWorkDir()) try: ret, out_stream, err_stream = rhn_popen(args % repr(password)) finally: chdir(cwd) out = sstr(out_stream.read()) out_stream.close() err = sstr(err_stream.read()) err_stream.close() if ret: # signature for a mistyped CA password if ( err.find("unable to load CA private key") != -1 and err.find( "error:0906A065:PEM routines:PEM_do_header:bad decrypt:pem_lib.c" ) != -1 and err.find( "error:06065064:digital envelope routines:EVP_DecryptFinal:bad decrypt:evp_enc.c" ) != -1 ): raise GenServerCertException( "web server's SSL certificate generation/signing " "failed:\nDid you mistype your CA password?" ) else: raise GenServerCertException( # pylint: disable-next=consider-using-f-string "web server's SSL certificate generation/signing " "failed:\n%s\n%s" % (out, err) ) if verbosity > 2: if out: print("STDOUT:", out) if err: print("STDERR:", err) # permissions: os.chmod(server_cert, int("0644", 8)) # cleanup duplicate XX.pem file: # pylint: disable-next=invalid-name pemFilename = os.path.basename(ser.upper() + ".pem") if pemFilename != server_cert and os.path.exists(pemFilename): os.unlink(pemFilename) # cleanup the old index.txt file try: os.unlink(index_txt + ".old") # pylint: disable-next=bare-except except: pass # cleanup the old serial file try: os.unlink(serial + ".old") # pylint: disable-next=bare-except except: pass def gen_jabberd_cert(d): """ generate the jabberd ssl cert from the server cert and key """ # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) server_key = os.path.join(serverKeyPairDir, d["--server-key"]) server_cert = os.path.join(serverKeyPairDir, d["--server-cert"]) dependencyCheck(server_key) dependencyCheck(server_cert) jabberd_ssl_cert_name = os.path.basename(d["--jabberd-ssl-cert"]) jabberd_ssl_cert = os.path.join(serverKeyPairDir, jabberd_ssl_cert_name) # Create the jabberd cert - need to concatenate the cert and the key # XXX there really should be some better error propagation here fd = None try: fd = os.open(jabberd_ssl_cert, os.O_WRONLY | os.O_CREAT) _copy_file_to_fd(cleanupAbsPath(server_cert), fd) _copy_file_to_fd(cleanupAbsPath(server_key), fd) finally: if fd: os.close(fd) return # pylint: disable-next=invalid-name def _disableRpmMacros(): mac = cleanupAbsPath("~/.rpmmacros") # pylint: disable-next=invalid-name macTmp = cleanupAbsPath("~/RENAME_ME_BACK_PLEASE-lksjdflajsd.rpmmacros") if os.path.exists(mac): os.rename(mac, macTmp) # pylint: disable-next=invalid-name def _reenableRpmMacros(): mac = cleanupAbsPath("~/.rpmmacros") # pylint: disable-next=invalid-name macTmp = cleanupAbsPath("~/RENAME_ME_BACK_PLEASE-lksjdflajsd.rpmmacros") if os.path.exists(macTmp): os.rename(macTmp, mac) # pylint: disable-next=invalid-name def genCaRpm_dependencies(d): """generates ssl cert RPM.""" gendir(d["--dir"]) ca_cert_name = os.path.basename(d["--ca-cert"]) ca_cert = os.path.join(d["--dir"], ca_cert_name) dependencyCheck(ca_cert) # pylint: disable-next=invalid-name def genCaRpm(d, verbosity=0): """generates ssl cert RPM.""" ca_cert_name = os.path.basename(d["--ca-cert"]) ca_cert = os.path.join(d["--dir"], ca_cert_name) ca_cert_rpm_name = os.path.basename(d["--ca-cert-rpm"]) ca_cert_rpm = os.path.join(d["--dir"], ca_cert_rpm_name) genCaRpm_dependencies(d) if verbosity >= 0: sys.stderr.write("\n...working...") # Work out the release number. hdr = getInstalledHeader(ca_cert_rpm) # find RPMs in the directory # pylint: disable-next=consider-using-f-string filenames = glob.glob("%s-*.noarch.rpm" % ca_cert_rpm) if filenames: filename = sortRPMs(filenames)[-1] h = get_package_header(filename) if hdr is None: hdr = h else: comp = hdrLabelCompare(h, hdr) if comp > 0: hdr = h # pylint: disable-next=unused-variable epo, ver, rel = None, "1.0", "0" if hdr is not None: epo, ver, rel = hdr["epoch"], hdr["version"], hdr["release"] # bump the release - and let's not be too smart about it # assume the release is a number. if rel: rel = str(int(rel) + 1) update_trust_script = os.path.join(CERT_PATH, "update-ca-cert-trust.sh") # build the CA certificate RPM args = ( # pylint: disable-next=consider-using-f-string os.path.join(CERT_PATH, "gen-rpm.sh") + " " "--name %s --version %s --release %s --packager %s --vendor %s " "--group 'RHN/Security' --summary %s --description %s " "--post %s --postun %s " "/usr/share/rhn/%s=%s" % ( repr(ca_cert_rpm_name), ver, rel, repr(d["--rpm-packager"]), repr(d["--rpm-vendor"]), repr(CA_CERT_RPM_SUMMARY), repr(CA_CERT_RPM_SUMMARY), repr(update_trust_script), repr(update_trust_script), repr(ca_cert_name), repr(cleanupAbsPath(ca_cert)), ) ) # pylint: disable-next=invalid-name,consider-using-f-string clientRpmName = "%s-%s-%s" % (ca_cert_rpm, ver, rel) if verbosity >= 0: print( # pylint: disable-next=consider-using-f-string """ Generating CA public certificate RPM: %s.src.rpm %s.noarch.rpm""" % (clientRpmName, clientRpmName) ) if verbosity > 1: print("Commandline:", args) _disableRpmMacros() cwd = chdir(d["--dir"]) try: ret, out_stream, err_stream = rhn_popen(args) except Exception: chdir(cwd) _reenableRpmMacros() raise chdir(cwd) _reenableRpmMacros() out = out_stream.read() out_stream.close() err = err_stream.read() err_stream.close() # pylint: disable-next=consider-using-f-string if ret or not os.path.exists("%s.noarch.rpm" % clientRpmName): raise GenCaCertRpmException( # pylint: disable-next=consider-using-f-string "CA public SSL certificate RPM generation " "failed:\n%s\n%s" % (out, err) ) if verbosity > 2: if out: print("STDOUT:", out) if err: print("STDERR:", err) # pylint: disable-next=consider-using-f-string os.chmod("%s.noarch.rpm" % clientRpmName, int("0644", 8)) # write-out latest.txt information latest_txt = os.path.join(d["--dir"], "latest.txt") fo = open(latest_txt, "wb") # pylint: disable-next=consider-using-f-string fo.write(bstr("%s\n" % ca_cert_name)) # pylint: disable-next=consider-using-f-string fo.write(bstr("%s.noarch.rpm\n" % os.path.basename(clientRpmName))) # pylint: disable-next=consider-using-f-string fo.write(bstr("%s.src.rpm\n" % os.path.basename(clientRpmName))) fo.close() os.chmod(latest_txt, int("0644", 8)) if verbosity >= 0: print( """ Make the public CA certificate publically available: (NOTE: the SUSE Manager Server or Proxy installers may do this step for you.) The "noarch" RPM and raw CA certificate can be made publically accessible by copying it to the /srv/www/htdocs/pub directory of your SUSE Manager Server or Proxy.""" ) # pylint: disable-next=consider-using-f-string return "%s.noarch.rpm" % clientRpmName # pylint: disable-next=invalid-name def genProxyServerTarball_dependencies(d): """dependency check for the step that generates the SUSE Manager Proxy's tar archive containing its SSL key set + CA certificate. """ # pylint: disable-next=invalid-name serverKeySetDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) gendir(serverKeySetDir) ca_cert = pathJoin(d["--dir"], d["--ca-cert"]) server_key = pathJoin(serverKeySetDir, d["--server-key"]) server_cert = pathJoin(serverKeySetDir, d["--server-cert"]) jabberd_ssl_cert = pathJoin(serverKeySetDir, d["--jabberd-ssl-cert"]) dependencyCheck(ca_cert) dependencyCheck(server_key) dependencyCheck(server_cert) dependencyCheck(jabberd_ssl_cert) # pylint: disable-next=invalid-name def getTarballFilename(d, version="1.0", release="1"): """figure out the current and next tar archive filename returns current, next (current can be None) """ # pylint: disable-next=invalid-name serverKeySetDir = pathJoin(d["--dir"], getMachineName(d["--set-hostname"])) server_tar_name = pathJoin(serverKeySetDir, d["--server-tar"]) # pylint: disable-next=consider-using-f-string filenames = glob.glob("%s-%s-*.tar" % (server_tar_name, version)) filenames.sort() # tested to be reliable versions = list(map(lambda x, n=len(server_tar_name): x[n + 1 : -4], filenames)) versions.sort() current = None if filenames: current = filenames[-1] # pylint: disable-next=redefined-builtin,consider-using-f-string next = "%s-%s-1.tar" % (server_tar_name, version) if current: v = versions[-1].split("-") v[-1] = str(int(v[-1]) + 1) # pylint: disable-next=consider-using-f-string next = "%s-%s.tar" % (server_tar_name, "-".join(v)) current = os.path.basename(current) # incoming release (usually coming from RPM version) is factored in # ...if RPM version-release is greater than that is used. v = next[len(server_tar_name) + 1 : -4] v = v.split("-") v[-1] = str(max(int(v[-1]), int(release))) # pylint: disable-next=consider-using-f-string next = "%s-%s.tar" % (server_tar_name, "-".join(v)) next = os.path.basename(next) return current, next # pylint: disable-next=invalid-name def genProxyServerTarball(d, version="1.0", release="1", verbosity=0): """generates the Spacewalk Proxy Server's tar archive containing its SSL key set + CA certificate """ genProxyServerTarball_dependencies(d) # pylint: disable-next=invalid-name tarballFilepath = getTarballFilename(d, version, release)[1] # pylint: disable-next=invalid-name tarballFilepath = pathJoin(d["--dir"], tarballFilepath) machinename = getMachineName(d["--set-hostname"]) tar_args = [ repr(os.path.basename(tarballFilepath)), repr(os.path.basename(d["--ca-cert"])), repr(pathJoin(machinename, d["--server-key"])), repr(pathJoin(machinename, d["--server-cert"])), repr(os.path.join(machinename, d["--jabberd-ssl-cert"])), ] # pylint: disable-next=invalid-name serverKeySetDir = pathJoin(d["--dir"], machinename) # pylint: disable-next=invalid-name tarballFilepath2 = pathJoin(serverKeySetDir, tarballFilepath) if verbosity >= 0: print( # pylint: disable-next=consider-using-f-string """ The most current SUSE Manager Proxy installation process against SUSE Manager hosted requires the upload of an SSL tar archive that contains the CA SSL public certificate and the web server's key set. Generating the web server's SSL key set and CA SSL public certificate archive: %s""" % tarballFilepath2 ) cwd = chdir(d["--dir"]) # check if (optional) cert request exists server_cert_req = os.path.join(machinename, d["--server-cert-req"]) if os.path.exists(server_cert_req): tar_args.append(repr(server_cert_req)) else: sys.stderr.write( # pylint: disable-next=consider-using-f-string "WARNING: Not bundling %s to server tarball (file " "not found)." % repr(server_cert_req) ) # build the server tarball # pylint: disable-next=consider-using-f-string args = ("/bin/tar -cvf %s " % " ".join(tar_args)).strip() try: if verbosity > 1: print("Current working directory:", os.getcwd()) print("Commandline:", args) ret, out_stream, err_stream = rhn_popen(args) finally: chdir(cwd) out = out_stream.read() out_stream.close() err = err_stream.read() err_stream.close() if ret or not os.path.exists(tarballFilepath): raise GenServerTarException( # pylint: disable-next=consider-using-f-string "CA SSL public certificate & web server's SSL key set tar archive\n" "generation failed:\n%s\n%s" % (out, err) ) if verbosity > 2: if out: print("STDOUT:", out) if err: print("STDERR:", err) # root baby! os.chmod(tarballFilepath, int("0600", 8)) # copy tarball into machine build dir shutil.copy2(tarballFilepath, tarballFilepath2) os.unlink(tarballFilepath) if verbosity > 1: print( # pylint: disable-next=consider-using-f-string """\ Moved to final home: %s ...moved to... %s""" % (tarballFilepath, tarballFilepath2) ) return tarballFilepath2 # pylint: disable-next=invalid-name def genServerRpm_dependencies(d): """generates server's SSL key set RPM - dependencies check""" # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) gendir(serverKeyPairDir) server_key_name = os.path.basename(d["--server-key"]) server_key = os.path.join(serverKeyPairDir, server_key_name) server_cert_name = os.path.basename(d["--server-cert"]) server_cert = os.path.join(serverKeyPairDir, server_cert_name) server_cert_req_name = os.path.basename(d["--server-cert-req"]) # pylint: disable-next=unused-variable server_cert_req = os.path.join(serverKeyPairDir, server_cert_req_name) jabberd_ssl_cert_name = os.path.basename(d["--jabberd-ssl-cert"]) # pylint: disable-next=unused-variable jabberd_ssl_cert = os.path.join(serverKeyPairDir, jabberd_ssl_cert_name) dependencyCheck(server_key) dependencyCheck(server_cert) gen_jabberd_cert(d) # pylint: disable-next=invalid-name def genServerRpm(d, verbosity=0): """generates server's SSL key set RPM""" # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) server_key_name = os.path.basename(d["--server-key"]) server_key = os.path.join(serverKeyPairDir, server_key_name) server_cert_name = os.path.basename(d["--server-cert"]) server_cert = os.path.join(serverKeyPairDir, server_cert_name) server_cert_req_name = os.path.basename(d["--server-cert-req"]) server_cert_req = os.path.join(serverKeyPairDir, server_cert_req_name) jabberd_ssl_cert_name = os.path.basename(d["--jabberd-ssl-cert"]) jabberd_ssl_cert = os.path.join(serverKeyPairDir, jabberd_ssl_cert_name) server_rpm_name = os.path.basename(d["--server-rpm"]) server_rpm = os.path.join(serverKeyPairDir, server_rpm_name) postun_scriptlet = os.path.join(d["--dir"], "postun.scriptlet") genServerRpm_dependencies(d) if verbosity >= 0: sys.stderr.write("\n...working...\n") # check for old installed RPM. # pylint: disable-next=invalid-name oldHdr = getInstalledHeader(LEGACY_SERVER_RPM_NAME1) if oldHdr and LEGACY_SERVER_RPM_NAME1 != server_rpm_name: sys.stderr.write( # pylint: disable-next=consider-using-f-string """ ** NOTE ** older-styled RPM installed (%s), it needs to be removed before installing the web server's RPM that is about to generated. """ % LEGACY_SERVER_RPM_NAME1 ) if not oldHdr: # pylint: disable-next=invalid-name oldHdr = getInstalledHeader(LEGACY_SERVER_RPM_NAME2) if oldHdr and LEGACY_SERVER_RPM_NAME2 != server_rpm_name: sys.stderr.write( # pylint: disable-next=consider-using-f-string """ ** NOTE ** older-styled RPM installed (%s), it needs to be removed before installing the web server's RPM that is about to generated. """ % LEGACY_SERVER_RPM_NAME2 ) # check for new installed RPM. # Work out the release number. hdr = getInstalledHeader(server_rpm_name) # find RPMs in the directory as well. # pylint: disable-next=consider-using-f-string filenames = glob.glob("%s-*.noarch.rpm" % server_rpm) if filenames: filename = sortRPMs(filenames)[-1] h = get_package_header(filename) if hdr is None: hdr = h else: comp = hdrLabelCompare(h, hdr) if comp > 0: hdr = h # pylint: disable-next=unused-variable epo, ver, rel = None, "1.0", "0" if hdr is not None: epo, ver, rel = hdr["epoch"], hdr["version"], hdr["release"] # bump the release - and let's not be too smart about it # assume the release is a number. if rel: rel = str(int(rel) + 1) description = ( SERVER_RPM_SUMMARY # pylint: disable-next=consider-using-f-string + """ Best practices suggests that this RPM should only be installed on the web server with this hostname: %s """ % d["--set-hostname"] ) # Determine which jabberd user exists: jabberd_user = None possible_jabberd_users = ["jabberd", "jabber"] for juser_attempt in possible_jabberd_users: try: pwd.getpwnam(juser_attempt) jabberd_user = juser_attempt # pylint: disable-next=bare-except except: # user doesn't exist, try the next pass if jabberd_user is None: print( "WARNING: No jabber/jabberd user on system, skipping " + "jabberd.pem generation." ) jabberd_cert_string = "" if jabberd_user is not None: # pylint: disable-next=consider-using-f-string jabberd_cert_string = "/etc/pki/spacewalk/jabberd/server.pem:0600,%s,%s=%s" % ( jabberd_user, jabberd_user, repr(cleanupAbsPath(jabberd_ssl_cert)), ) ## build the server RPM args = ( # pylint: disable-next=consider-using-f-string os.path.join(CERT_PATH, "gen-rpm.sh") + " " "--name %s --version %s --release %s --packager %s --vendor %s " "--group 'RHN/Security' --summary %s --description %s --postun %s " "/etc/httpd/conf/ssl.key/server.key:0600=%s " "/etc/httpd/conf/ssl.crt/server.crt=%s " "%s " % ( repr(server_rpm_name), ver, rel, repr(d["--rpm-packager"]), repr(d["--rpm-vendor"]), repr(SERVER_RPM_SUMMARY), repr(description), repr(cleanupAbsPath(postun_scriptlet)), repr(cleanupAbsPath(server_key)), repr(cleanupAbsPath(server_cert)), jabberd_cert_string, ) ) abs_server_cert_req = cleanupAbsPath(server_cert_req) if os.path.exists(abs_server_cert_req): # pylint: disable-next=consider-using-f-string args += "/etc/httpd/conf/ssl.csr/server.csr=%s" % repr(abs_server_cert_req) else: sys.stderr.write( # pylint: disable-next=consider-using-f-string "WARNING: Not bundling %s to server RPM " "(file not found)." % repr(server_cert_req) ) # pylint: disable-next=invalid-name,consider-using-f-string serverRpmName = "%s-%s-%s" % (server_rpm, ver, rel) if verbosity >= 0: print( # pylint: disable-next=consider-using-f-string """ Generating web server's SSL key pair/set RPM: %s.src.rpm %s.noarch.rpm""" % (serverRpmName, serverRpmName) ) if verbosity > 1: print("Commandline:", args) if verbosity >= 4: print("Current working directory:", os.getcwd()) print("Writing postun_scriptlet:", postun_scriptlet) # pylint: disable-next=unspecified-encoding open(postun_scriptlet, "w").write(POST_UNINSTALL_SCRIPT) _disableRpmMacros() cwd = chdir(serverKeyPairDir) try: ret, out_stream, err_stream = rhn_popen(args) finally: chdir(cwd) _reenableRpmMacros() os.unlink(postun_scriptlet) out = out_stream.read() out_stream.close() err = err_stream.read() err_stream.close() # pylint: disable-next=consider-using-f-string if ret or not os.path.exists("%s.noarch.rpm" % serverRpmName): raise GenServerRpmException( # pylint: disable-next=consider-using-f-string "web server's SSL key set RPM generation " "failed:\n%s\n%s" % (out, err) ) if verbosity > 2: if out: print("STDOUT:", out) if err: print("STDERR:", err) # pylint: disable-next=consider-using-f-string os.chmod("%s.noarch.rpm" % serverRpmName, int("0600", 8)) # generic the tarball necessary for Spacewalk Proxy against hosted installations # pylint: disable-next=invalid-name tarballFilepath = genProxyServerTarball( d, version=ver, release=rel, verbosity=verbosity ) # write-out latest.txt information latest_txt = os.path.join(serverKeyPairDir, "latest.txt") fo = open(latest_txt, "wb") # pylint: disable-next=consider-using-f-string fo.write(bstr("%s.noarch.rpm\n" % os.path.basename(serverRpmName))) # pylint: disable-next=consider-using-f-string fo.write(bstr("%s.src.rpm\n" % os.path.basename(serverRpmName))) # pylint: disable-next=consider-using-f-string fo.write(bstr("%s\n" % os.path.basename(tarballFilepath))) fo.close() os.chmod(latest_txt, int("0600", 8)) if verbosity >= 0: print( # pylint: disable-next=consider-using-f-string """ Deploy the server's SSL key pair/set RPM: (NOTE: the SUSE Manager or Proxy installers may do this step for you.) The "noarch" RPM needs to be deployed to the machine working as a web server, or SUSE Manager, or SUSE Manager Proxy. Presumably %s.""" % repr(d["--set-hostname"]) ) # pylint: disable-next=consider-using-f-string return "%s.noarch.rpm" % serverRpmName # Helper function def _copy_file_to_fd(filename, fd): # pylint: disable-next=unspecified-encoding f = open(filename) buffer_size = 16384 count = 0 while 1: buf = f.read(buffer_size) if not buf: break os.write(fd, bstr(buf)) count = count + len(buf) return count # pylint: disable-next=invalid-name def genServer_dependencies(password, d): """deps for the general --gen-server command. I.e., generation of server.{key,csr,crt}. """ ca_key_name = os.path.basename(d["--ca-key"]) ca_key = os.path.join(d["--dir"], ca_key_name) ca_cert_name = os.path.basename(d["--ca-cert"]) ca_cert = os.path.join(d["--dir"], ca_cert_name) dependencyCheck(ca_key) dependencyCheck(ca_cert) if password is None: sys.stderr.write("ERROR: a CA password must be supplied.\n") sys.exit(errnoGeneralError) # pylint: disable-next=invalid-name def checkCaKey(password, d, verbosity=0): """check CA key's password""" ca_key = os.path.join(d["--dir"], os.path.basename(d["--ca-key"])) # pylint: disable-next=consider-using-f-string args = "/usr/bin/openssl rsa -in %s -check -passin pass:%s" % ( repr(cleanupAbsPath(cleanupAbsPath(ca_key))), "%s", ) if verbosity >= 0: # pylint: disable-next=consider-using-f-string print("\nChecking private CA key's password: %s" % ca_key) if verbosity > 1: print("Commandline:", args % "PASSWORD") ret, out_stream, err_stream = rhn_popen(args % repr(password)) out = out_stream.read() out_stream.close() err = err_stream.read() err_stream.close() if ret: raise GenPrivateCaKeyException( # pylint: disable-next=consider-using-f-string "Certificate Authority private " "key's password does not match or " "key broken:\n%s\n" "%s" % (out, err) ) # pylint: disable-next=invalid-name def checkCaCert(d, verbosity=0): """check CA key's password""" ca_cert = os.path.join(d["--dir"], os.path.basename(d["--ca-cert"])) # pylint: disable-next=consider-using-f-string args = "/usr/bin/openssl x509 -in %s -noout" % ( repr(cleanupAbsPath(cleanupAbsPath(ca_cert))) ) if verbosity >= 0: # pylint: disable-next=consider-using-f-string print("\nChecking CA cert's validity: %s" % ca_cert) if verbosity > 1: print("Commandline:", args) ret, out_stream, err_stream = rhn_popen(args) out = out_stream.read() out_stream.close() err = err_stream.read() err_stream.close() if ret: raise GenPrivateCaKeyException( # pylint: disable-next=consider-using-f-string "Certificate Authority certificate " "does not exist or is broken:\n%s\n" "%s" % (out, err) ) def _copy_ca_file(d, f): if not os.path.isfile(f): raise GenCaCertRpmException( # pylint: disable-next=consider-using-f-string "CA public SSL certificate RPM generation " "failed: file %s not found." % f ) gendir(d["--dir"]) ca_cert_name = os.path.basename(d["--ca-cert"]) ca_cert = os.path.join(d["--dir"], ca_cert_name) shutil.copy2(f, ca_cert) def _copy_server_ssl_key(d, key_file): if not os.path.isfile(key_file): raise GenServerRpmException( # pylint: disable-next=consider-using-f-string "web server's SSL key set RPM generation " "failed: file %s not found." % key_file ) # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) gendir(serverKeyPairDir) server_key_name = os.path.basename(d["--server-key"]) server_key = os.path.join(serverKeyPairDir, server_key_name) shutil.copy2(key_file, server_key) def _copy_server_ssl_cert(d, cert_file): if not os.path.isfile(cert_file): raise GenServerRpmException( # pylint: disable-next=consider-using-f-string "web server's SSL key set RPM generation " "failed: file %s not found." % cert_file ) # pylint: disable-next=invalid-name serverKeyPairDir = os.path.join(d["--dir"], getMachineName(d["--set-hostname"])) gendir(serverKeyPairDir) server_cert_name = os.path.basename(d["--server-cert"]) server_cert = os.path.join(serverKeyPairDir, server_cert_name) shutil.copy2(cert_file, server_cert) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def _main(): """main routine""" options = processCommandline() legacyTreeFixup(DEFS) if getOption(options, "check_key"): checkCaKey(getCAPassword(options), DEFS) if getOption(options, "check_cert"): checkCaCert(DEFS) if getOption(options, "gen_ca"): if getOption(options, "key_only"): genPrivateCaKey( getCAPassword(options), DEFS, options.verbose, options.force ) elif getOption(options, "cert_only"): genPublicCaCert_dependencies(getCAPassword(options), DEFS, options.force) genPublicCaCert( getCAPassword(options), DEFS, options.verbose, options.force ) elif getOption(options, "rpm_only"): if getOption(options, "from_ca_cert"): _copy_ca_file(DEFS, getOption(options, "from_ca_cert")) genCaRpm_dependencies(DEFS) genCaRpm(DEFS, options.verbose) else: genPrivateCaKey( getCAPassword(options), DEFS, options.verbose, options.force ) genPublicCaCert( getCAPassword(options), DEFS, options.verbose, options.force ) if not getOption(options, "no_rpm"): genCaRpm(DEFS, options.verbose) if getOption(options, "gen_server"): if getOption(options, "key_only"): genServerKey(DEFS, options.verbose) elif getOption(options, "cert_req_only"): genServerCertReq_dependencies(DEFS) genServerCertReq(DEFS, options.verbose) elif getOption(options, "cert_only"): genServerCert_dependencies(getCAPassword(options, confirmYN=0), DEFS) genServerCert(getCAPassword(options, confirmYN=0), DEFS, options.verbose) elif getOption(options, "rpm_only"): if getOption(options, "from_server_key"): _copy_server_ssl_key(DEFS, getOption(options, "from_server_key")) if getOption(options, "from_server_cert"): _copy_server_ssl_cert(DEFS, getOption(options, "from_server_cert")) genServerRpm_dependencies(DEFS) genServerRpm(DEFS, options.verbose) else: genServer_dependencies(getCAPassword(options, confirmYN=0), DEFS) genServerKey(DEFS, options.verbose) genServerCertReq(DEFS, options.verbose) genServerCert(getCAPassword(options, confirmYN=0), DEFS, options.verbose) gen_jabberd_cert(DEFS) if not getOption(options, "no_rpm"): genServerRpm(DEFS, options.verbose) def main(): """main routine wrapper (exception handler) 1 general error 10 private CA key generation error 11 public CA certificate generation error 12 public CA certificate RPM build error 20 private web server key generation error 21 public web server certificate request generation error 22 public web server certificate generation error 23 web server key pair/set RPM build error 30 Certificate expiration too short exception 31 Certificate expiration too long exception (integer in days range: 1 to # days til 1 year before the 32-bit overflow) 32 country code length cannot exceed 2 33 missing file created in previous step 100 general SUSE Manager SSL tool error """ # pylint: disable-next=invalid-name def writeError(e): # pylint: disable-next=consider-using-f-string sys.stderr.write("\nERROR: %s\n" % e) ret = 0 try: ret = _main() or 0 # CA key set errors except GenPrivateCaKeyException as e: writeError(e) ret = 10 except GenPublicCaCertException as e: writeError(e) ret = 11 except GenCaCertRpmException as e: writeError(e) ret = 12 # server key set errors except GenServerKeyException as e: writeError(e) ret = 20 except GenServerCertReqException as e: writeError(e) ret = 21 except GenServerCertException as e: writeError(e) ret = 22 except GenServerRpmException as e: writeError(e) ret = 23 # other errors except CertExpTooShortException as e: writeError(e) ret = 30 except CertExpTooLongException as e: writeError(e) ret = 31 except InvalidCountryCodeException as e: writeError(e) ret = 32 except FailedFileDependencyException as e: # already wrote a nice error message # pylint: disable-next=consider-using-f-string msg = """\ can't find a file that should have been created during an earlier step: %s %s --help""" % ( e, os.path.basename(sys.argv[0]), ) writeError(msg) ret = 33 except RhnSslToolException as e: writeError(e) ret = 100 return ret # ------------------------------------------------------------------------------- if __name__ == "__main__": sys.stderr.write( "\nWARNING: intended to be wrapped by another executable\n" " calling program.\n" ) sys.exit(abs(main() or errnoSuccess)) # =============================================================================== 07070100000015000081ED000003E80000006400000001662798DF00002287000000000000000000000000000000000000001E00000000spacewalk-certs-tools/sign.sh#!/bin/bash # # Copyright (c) 2008--2013 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # script that signs a certificate request with a CA key # The script expects to find ca.key, ca.crt and server.csr in the working # directory # The signed certificate is saved in server.crt # server.crt and server.key are packaged as rhn-httpd-ssl-key-pair-*.rpm # RHN-ORG-TRUSTED-SSL-CERT (or ca.cert) is packaged as # rhn-org-trusted-ssl-cert-*.rpm # TOPDIR=$(cd $(dirname $0) && pwd) PASSWORD= STARTDATE= # 1 year. EXP_DAYS=365 CA_KEY=ca.key CA_CRT=ca.crt SERVER_KEY=server.key SERVER_CSR=server.csr CLIENT_RPM_NAME="rhn-org-trusted-ssl-cert" SERVER_RPM_NAME="rhn-httpd-ssl-key-pair" SSL_BACKUP_TARBALL_NAME="rhn-org-ssl-backup" usage() { echo "Usage:" echo "$0 [OPTIONS]" echo "Signs a server certificate request with a CA key" echo echo " OPTIONS:" echo " --help display usage and exit" echo " --topdir path to gen-rpm.sh [$TOPDIR]" echo " --password password for the CA key [read from stdin]" echo " --startdate start date; format: YYMMDDHHMMSSZ (the letter Z)" echo " --days number of days the cert is valid" echo " --ca-key Certificate Authority private key" echo " --ca-crt Certificate Authority certificate" echo " --server-key Server (httpd) private key" echo " --server-csr Server (httpd) Certificate Signing Request" echo " --server-crt Server (httpd) Certificate" echo " --ca-rpm RPM name for the CA certificate" echo " (default: $CLIENT_RPM_NAME)" echo " --server-rpm RPM name for the server's (httpd) SSL key/cert pair" echo " (default: $SERVER_RPM_NAME)" echo " --openssl-conf Use this OpenSSL configuration file" echo exit $1 } while [ ${#} -ne 0 ]; do arg="$1" shift case "$arg" in --help) usage 0 ;; --topdir) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) TOPDIR="$1" shift;; --password) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) PASSWORD="$1" shift;; --startdate) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) STARTDATE="$1" shift;; --days) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) EXP_DAYS="$1" shift;; --ca-key) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) CA_KEY="$1" shift;; --ca-crt) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) CA_CRT="$1" shift;; --server-key) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) SERVER_KEY="$1" shift;; --server-csr) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) SERVER_CSR="$1" shift;; --ca-rpm) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) CLIENT_RPM_NAME="$1" shift;; --server-rpm) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) SERVER_RPM_NAME="$1" shift;; --openssl-conf) [ ${#} -eq 0 ] && (echo "No parameter specified for $arg" >&2; exit 1) OPENSSL_CONF="$1" shift;; "") break;; *) echo "Extra parameter $1 ignored" shift esac done gen_openssl_conf() { cat > openssl.cnf << EOF [ ca ] default_ca = CA_default # The default ca section [ CA_default ] serial = serial # The current serial number database = index.txt # Commenting for now, the cert gets too big x509_extensions = usr_cert copy_extensions = copy # For the CA policy [ policy_match ] countryName = match stateOrProvinceName = optional organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ usr_cert ] basicConstraints = CA:false extendedKeyUsage = serverAuth,clientAuth nsCertType = server keyUsage = digitalSignature, keyEncipherment # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer:always EOF } if [ -z "$OPENSSL_CONF" ]; then gen_openssl_conf OPENSSL_CONF=openssl.cnf fi # Error checking - make sure all the needed files are there for fn in $CA_KEY $CA_CRT $SERVER_KEY $SERVER_CSR $OPENSSL_CONF; do if [ ! -f ${fn} ]; then echo "Error: Could not find required file ${fn}" exit 1 fi if [ ! -s ${fn} ] ; then echo "Error: Zero-length file ${fn}" exit 2 fi done GENRPMFILE=gen-rpm.sh if [ ! -f $TOPDIR/$GENRPMFILE ]; then echo "$TOPDIR/$GENRPMFILE not found; please use --topdir" exit 1 fi GENRPM="sh $TOPDIR/$GENRPMFILE" [ -f serial ] || echo 01 > serial echo -n > index.txt [ -z $STARTDATE ] || STARTDATE="-startdate $STARTDATE" # If we've been supplied a password on the command line, use it if [ -n "$PASSWORD" ]; then /usr/bin/openssl ca -config $OPENSSL_CONF -in $SERVER_CSR -out server.crt \ -outdir . -batch -cert $CA_CRT -keyfile $CA_KEY \ $STARTDATE -days $EXP_DAYS -md md5 -policy policy_match \ -passin "pass:$PASSWORD" else /usr/bin/openssl ca -config $OPENSSL_CONF -in $SERVER_CSR -out server.crt \ -outdir . -batch -cert $CA_CRT -keyfile $CA_KEY \ $STARTDATE -days $EXP_DAYS -md md5 -policy policy_match fi if [ $? -ne 0 ]; then echo "Error: unable to generate server.crt" exit $? fi # Get the version and release for this package version=`rpm -q --qf "%{version}" ${SERVER_RPM_NAME}` # If the result code is non-zero, the package is not installed [ $? -eq 0 ] || version="1.0" release=`rpm -q --qf "%{release}" ${SERVER_RPM_NAME}` if [ $? -eq 0 ]; then release=`echo $release | sed "s/^\([0-9]*\).*$/\1/"` else # package not installed release=0 fi # Bump the release release=$[$release+1] PROD_NAME=`spacewalk-cfg-get get web product_name` EMAIL=`spacewalk-cfg-get traceback_mail` PACKAGER=$PROD_NAME$EMAIL # Generate a postun scriptlet cat > postun.scriptlet << EOSCRIPTLET if [ \$1 = 0 ]; then # The following steps are copied from mod_ssl's postinstall scriptlet # Make sure the permissions are okay umask 077 if [ ! -f /etc/httpd/conf/ssl.key/server.key ] ; then /usr/bin/openssl genrsa -rand /proc/apm:/proc/cpuinfo:/proc/dma:/proc/filesystems:/proc/interrupts:/proc/ioports:/proc/pci:/proc/rtc:/proc/uptime 1024 > /etc/httpd/conf/ssl.key/server.key 2> /dev/null fi if [ ! -f /etc/httpd/conf/ssl.crt/server.crt ] ; then cat << EOF | /usr/bin/openssl req -new -key /etc/httpd/conf/ssl.key/server.key -x509 -days 365 -out /etc/httpd/conf/ssl.crt/server.crt 2>/dev/null -- SomeState SomeCity SomeOrganization SomeOrganizationalUnit localhost.localdomain root@localhost.localdomain EOF fi /sbin/service httpd graceful || /sbin/service httpd try-restart exit 0 fi EOSCRIPTLET # Package the server's cert and private key $GENRPM --name $SERVER_RPM_NAME --version $version \ --release $release --packager "$PACKAGER" \ --summary "server private SSL key and certificate for the $PROD_NAME" \ --description "server private SSL key and certificate for the $PROD_NAME" \ --postun postun.scriptlet \ /etc/httpd/conf/ssl.crt/server.crt=server.crt \ /etc/httpd/conf/ssl.key/server.key:0600=${SERVER_KEY} || exit 1 chmod 0600 ${SERVER_RPM_NAME}-${version}-${release}.{src,noarch}.rpm rm -f postun.scriptlet # Now that we have the CA cert generated, let's package it into an rpm $GENRPM --name ${CLIENT_RPM_NAME} --version $version \ --release $release --packager "${PACKAGER}" \ --summary "CA SSL certificate for the $PROD_NAME (client-side)" \ --description "CA SSL certificate for the $PROD_NAME (client-side)" \ /usr/share/rhn/${CA_CRT}=${CA_CRT} || exit 1 07070100000016000081A4000003E80000006400000001662798DF000083C3000000000000000000000000000000000000003400000000spacewalk-certs-tools/spacewalk-certs-tools.changes------------------------------------------------------------------- Tue Feb 13 17:26:32 CET 2024 - marina.latini@suse.com - version 5.0.3-1 * Skip deploying the CA into the salt dir on proxies (bsc#1219850) * Deploy the CA certificate also into the salt filesystem (bsc#1219577) ------------------------------------------------------------------- Mon Jan 29 11:47:08 CET 2024 - rosuna@suse.com - version 5.0.2-1 * Apply Black and Pylint to enforce Python style ------------------------------------------------------------------- Tue Jan 16 08:19:04 CET 2024 - jgonzalez@suse.com - version 5.0.1-1 * Bump version to 5.0.0 ------------------------------------------------------------------- Fri Dec 15 17:07:23 CET 2023 - rosuna@suse.com - version 4.4.9-1 * Include reboot info beacon in the bootstrap script for transactional systems (bsc#1217588) * Enable openSUSE Tumbleweed and openSUSE MicroOS * Move all file managed by RPM from /srv to /usr/share/susemanager * Drop spacewalk-ssh-push-init and spacewalk-push-register * Change certificate defaults using RSA 4096 Bit and sha384 hash algorithm * Change wrong name in unexisting variable ORG_CERT_FILE to CERT_FILE for rhn_bootstrap_strings.py * Revert hardcoded folder and renamed variable for clarity ------------------------------------------------------------------- Wed Nov 01 20:43:30 CET 2023 - marina.latini@suse.com - version 4.4.8-1 * Handle server keys in PKCS8 format in mgr-ssl-cert-setup (bsc#1218615) ------------------------------------------------------------------- Mon Sep 18 14:28:41 CEST 2023 - rosuna@suse.com - version 4.4.7-1 * mgr-ssl-cert-setup: store CA certificate in database (bsc#1212856) * support EC Cryptography with mgr-ssl-cert-setup * Revert openssl3 compatibility because it breaks cert validation * Remove server keys to allow reregistering to different master * mgr-bootstrap read the hostname from rhn.conf if possible * Remove client_config_update.py * Add openssl3 compatibility. * Read CA password from a file * Also ship SUSE specific files on Enterprise Linux. * Use the CA cert in the pki config to generate build host rpm * Add shadow as dependency of osimage certificate package (bsc#1210834 bsc#1204089) ------------------------------------------------------------------- Wed Apr 19 12:52:17 CEST 2023 - marina.latini@suse.com - version 4.4.6-1 * remove unused dependencies ------------------------------------------------------------------- Tue Feb 21 14:03:19 CET 2023 - jgonzalez@suse.com - version 4.4.5-1 * Ensure installation of make for building ------------------------------------------------------------------- Mon Jan 23 08:27:21 CET 2023 - jgonzalez@suse.com - version 4.4.4-1 * add transactional system support to the bootstrap generator * change bootstrap script generator to detect SLE Micro ------------------------------------------------------------------- Wed Dec 14 14:12:51 CET 2022 - jgonzalez@suse.com - version 4.4.3-1 * remove jabberd and osa-dispatcher * drop legacy way to prevent disabling local repos ------------------------------------------------------------------- Fri Nov 18 15:04:43 CET 2022 - jgonzalez@suse.com - version 4.4.2-1 * Generated bootstrap scripts installs all needed Salt 3004 dependencies for Ubuntu 18.04 (bsc#1204517) * drop traditional from bootstrap script ------------------------------------------------------------------- Wed Sep 28 11:04:19 CEST 2022 - jgonzalez@suse.com - version 4.4.1-1 * fix mgr-ssl-cert-setup for root CAs which do not set authorityKeyIdentifier (bsc#1203585) ------------------------------------------------------------------- Wed Jul 27 14:13:56 CEST 2022 - jgonzalez@suse.com - version 4.3.14-1 * traditional stack bootstrap: install product packages (bsc#1201142) * display messages to restart services after certificate change * improve CA Chain checking by comparing authorityKeyIdentifier with subjectKeyIdentifier ------------------------------------------------------------------- Thu Jun 09 13:43:22 CEST 2022 - jgonzalez@suse.com - version 4.3.13-1 * set permissions on apache ssl key file (bsc#1200371) ------------------------------------------------------------------- Wed Jun 01 10:01:23 CEST 2022 - jgonzalez@suse.com - version 4.3.12-1 * use RES bootstrap repo as a fallback for Red Hat downstream OS (bsc#1200087) ------------------------------------------------------------------- Tue Apr 19 11:59:08 CEST 2022 - jgonzalez@suse.com - version 4.3.11-1 * deploy local CA under different name in the truststore to avoid conflicts with CAs deployed during a registration * Adapted openssl call in mgr_ssl_cert_setup.py to work on Python 3.9. * Add randomness to first generated server serial ------------------------------------------------------------------- Thu Mar 31 15:55:24 CEST 2022 - jgonzalez@suse.com - version 4.3.10-1 * Use _arch instead of _host_cpu macro to detect the arch of the Salt Bundle to be deployed (bsc#1197759) ------------------------------------------------------------------- Tue Mar 15 16:30:36 CET 2022 - jgonzalez@suse.com - version 4.3.9-1 * compare timestamps in the right timezone ------------------------------------------------------------------- Fri Mar 11 16:48:15 CET 2022 - jgonzalez@suse.com - version 4.3.8-1 * Generate openssl CA configuration if missing when creating server certificate ------------------------------------------------------------------- Fri Mar 11 15:47:31 CET 2022 - jgonzalez@suse.com - version 4.3.7-1 * Fixed use of update-ca-cert-trust.sh in RPM post script ------------------------------------------------------------------- Fri Mar 11 14:46:51 CET 2022 - jgonzalez@suse.com - version 4.3.6-1 * Do not generate Salt Bundle sections in bootstrap for traditional * Reuse certificate code. * Allow alternative certificate filenames for update-ca-cert-trust.sh. * Add dynamic version for bootstrap script header (bsc#1186336) * support checking proxy containers TLS certificates ------------------------------------------------------------------- Tue Feb 15 10:02:00 CET 2022 - jgonzalez@suse.com - version 4.3.5-1 * add new setup tool to check and deploy TLS certificates for Uyuni/SUSE Manager Server and Proxy ------------------------------------------------------------------- Tue Jan 18 13:51:12 CET 2022 - jgonzalez@suse.com - version 4.3.4-1 * Make bootstrap script to use bash when called with a different interpreter (bsc#1191656) ------------------------------------------------------------------- Fri Dec 03 12:21:24 CET 2021 - jgonzalez@suse.com - version 4.3.3-1 * Allow "--force-bundle" attribute when generating bootstrap script ------------------------------------------------------------------- Fri Nov 05 13:36:40 CET 2021 - jgonzalez@suse.com - version 4.3.2-1 * set key format to PEM when generating key for traditional clients push ssh (bsc#1189643) * add GPG keys using apt-key on debian machines (bsc#1187998) ------------------------------------------------------------------- Mon Aug 09 10:58:52 CEST 2021 - jgonzalez@suse.com - version 4.3.1-1 - Bootstrap script generator modified to handle new placement of salt bundle - added support of bootstrapping Raspbian 10 and 9 with bootstrap script - added support of bootstrapping with salt bundle - Prepare the bootstrap script generator for Rocky Linux 8 - Removed Python 2 build. - Updated source URL reference. - generate SSL private keys FIPS 140-2 compatible (bsc#1187593) ------------------------------------------------------------------- Thu Jun 10 13:45:38 CEST 2021 - jgonzalez@suse.com - version 4.2.10-1 - added error message when re-running configure-proxy.sh and chosing the option not to import the existing certificates ------------------------------------------------------------------- Wed Jun 09 10:20:18 CEST 2021 - jgonzalez@suse.com - version 4.2.9-1 - added correct SUMA version in traditional client bootstrap script ------------------------------------------------------------------- Mon May 24 12:36:36 CEST 2021 - jgonzalez@suse.com - version 4.2.8-1 - Add support of DISABLE_LOCAL_REPOS=0 for salt minions (bsc#1185568) - Add missing environment variable SALT_RUNNING for pkg module to the minion configuration ------------------------------------------------------------------- Wed May 05 16:33:57 CEST 2021 - jgonzalez@suse.com - version 4.2.7-1 - Fix typo: activaion -> activation ------------------------------------------------------------------- Fri Apr 16 13:21:19 CEST 2021 - jgonzalez@suse.com - version 4.2.6-1 - Prepare the bootstrap script generator for AlmaLinux 8 - Prepare the bootstrap script generator for Amazon Linux 2 - Prepare the bootstrap script generator for Alibaba Cloud Linux 2 ------------------------------------------------------------------- Thu Feb 25 12:04:35 CET 2021 - jgonzalez@suse.com - version 4.2.5-1 - Add reactivation key support to bootstrap script (bsc#1181580) ------------------------------------------------------------------- Wed Jan 27 13:02:05 CET 2021 - jgonzalez@suse.com - version 4.2.4-1 - drop the --noSSLServerURL option ------------------------------------------------------------------- Thu Dec 03 13:44:00 CET 2020 - jgonzalez@suse.com - version 4.2.3-1 - Added RHEL8 build support. - SPEC file house keeping. ------------------------------------------------------------------- Wed Nov 25 12:20:25 CET 2020 - jgonzalez@suse.com - version 4.2.2-1 - improve check for correct CA trust store directory (bsc#1176417) - Add option --notty to spacewalk-ssh-push-init ------------------------------------------------------------------- Fri Sep 18 12:15:29 CEST 2020 - jgonzalez@suse.com - version 4.2.1-1 - Add option --nostricthostkeychecking to spacewalk-ssh-push-init - Fix the fallback to RES bootstrap repo for Centos (bsc#1174423) - strip SSL Certificate Common Name after 63 Characters (bsc#1173535) - Update package version to 4.2.0 ------------------------------------------------------------------- Thu Jul 23 13:26:12 CEST 2020 - jgonzalez@suse.com - version 4.1.11-1 - fix centos detection (bsc#1173584) ------------------------------------------------------------------- Wed Jul 01 16:11:36 CEST 2020 - jgonzalez@suse.com - version 4.1.10-1 - Use RES bootstrap repository as fallback repo when bootstrapping CentOS (bsc#1173556) ------------------------------------------------------------------- Wed Jun 10 12:15:57 CEST 2020 - jgonzalez@suse.com - version 4.1.9-1 - Enable bootstrapp scripts for Oracle Linux 6, 7 and 8 ------------------------------------------------------------------- Wed May 20 10:54:23 CEST 2020 - jgonzalez@suse.com - version 4.1.8-1 - On Debian-like systems, install only required dependencies when installing salt - Fix OS detection for Ubuntu in bootstrap script ------------------------------------------------------------------- Mon Apr 13 09:33:21 CEST 2020 - jgonzalez@suse.com - version 4.1.7-1 - Enable support for bootstrapping Astra Linux CE "Orel" ------------------------------------------------------------------- Thu Mar 19 12:08:00 CET 2020 - jgonzalez@suse.com - version 4.1.6-1 - Enable support for bootstrapping Debian 9 and 10 ------------------------------------------------------------------- Wed Mar 11 10:50:54 CET 2020 - jgonzalez@suse.com - version 4.1.5-1 - add minion option in config file to disable salt mine when generated by bootstrap script (bsc#1163001) - Disable modularity failsafe mechanism for RHEL 8 bootstrap repos (bsc#1164875) - Add 'start_event_grains' minion option to configfile when generated by bootstrap script - forbid multiple activation keys for salt minions during bootstrap (bsc#1164452) ------------------------------------------------------------------- Mon Feb 17 12:48:49 CET 2020 - jgonzalez@suse.com - version 4.1.4-1 - fix --help output for mgr-ssl-tool and mgr-bootstrap (bsc#1010746) - fix manpages for mgr-ssl-tool and mgr-bootstrap (bsc#1010746) ------------------------------------------------------------------- Wed Jan 22 12:11:44 CET 2020 - jgonzalez@suse.com - version 4.1.3-1 - revert fix for bsc#1152795 and add special handling for detecting SLES ES6 systems; hacky due to special release file with unexpected contents (bsc#1132576) - add additional minion options to configfile when generated by bootstrap script (bsc#1159492) - Change the order to check the version correctly for RES (bsc#1152795) ------------------------------------------------------------------- Thu Nov 28 17:54:02 CET 2019 - jgonzalez@suse.com - version 4.1.2-1 - fix bootstrap script generator to work with Expanded Support 8 product (bsc#1158002) ------------------------------------------------------------------- Wed Nov 27 17:00:05 CET 2019 - jgonzalez@suse.com - version 4.1.1-1 - Fix certificate generation when the serial has leading zeroes to avoid "asn1 encoding routines:a2i_ASN1_INTEGER:odd number of chars" during setup - require uyuni-common-libs - Bump version to 4.1.0 (bsc#1154940) - make traditional bootstrap more robust for unknown hostname (bsc#1152298) - Require mgr-daemon (new name of spacewalksd) so we systems with spacewalksd get always the new package installed (bsc#1149353) ------------------------------------------------------------------- Wed Jul 31 17:32:10 CEST 2019 - jgonzalez@suse.com - version 4.0.10-1 - Run bootstrap.sh completely unattended on Ubuntu (bsc#1137881) - Add new packages names to instructions for adding remote commands support for traditional clients (bsc#1137255) ------------------------------------------------------------------- Tue May 21 10:56:53 CEST 2019 - jgonzalez@suse.com - version 4.0.9-1 - fix missing quotation in bootstrap script (bsc#1135659) ------------------------------------------------------------------- Wed May 15 15:08:39 CEST 2019 - jgonzalez@suse.com - version 4.0.8-1 - SPEC cleanup - Add new packages names to instructions for adding remote configuration support for traditional clients - Print error message instead of stacktrace for client_config_update.py ------------------------------------------------------------------- Mon Apr 22 12:08:38 CEST 2019 - jgonzalez@suse.com - version 4.0.7-1 - Generate SLE11 specific ssl-cert-osimage package - Add support for Ubuntu to bootstrap script - Add makefile and pylintrc for PyLint ------------------------------------------------------------------- Fri Mar 29 10:31:16 CET 2019 - jgonzalez@suse.com - version 4.0.6-1 - Prevent encoding issues when exceptions are triggered. ------------------------------------------------------------------- Wed Feb 27 13:00:34 CET 2019 - jgonzalez@suse.com - version 4.0.5-1 - clean up downloaded gpg keys after bootstrap (bsc#1126075) - Fix problem with spacewalk certs tools and Python3 (bsc#1125282) ------------------------------------------------------------------- Wed Jan 16 12:22:08 CET 2019 - jgonzalez@suse.com - version 4.0.4-1 - Fix python 2/3 dependencies in spacewalk-certs-tools ------------------------------------------------------------------- Mon Dec 17 14:35:42 CET 2018 - jgonzalez@suse.com - version 4.0.3-1 - Add client packages for Yum based distributions - enhance bootstrap-repo urls for Centos and Opensuse ------------------------------------------------------------------- Fri Oct 26 10:07:59 CEST 2018 - jgonzalez@suse.com - version 4.0.2-1 - Add support for Python 3 (bsc#1102528) ------------------------------------------------------------------- Fri Aug 10 15:14:30 CEST 2018 - jgonzalez@suse.com - version 4.0.1-1 - Bump version to 4.0.0 (bsc#1104034) - Feat: check for Dynamic CA-Trust Updates while bootstrapping on RES (FATE #325588) - Feat: add OS Image building with Kiwi FATE#322959 FATE#323057 FATE#323056 - Fix copyright for the package specfile (bsc#1103696) - Enable Spacewalk 2.8 client tools for using with Uyuni ------------------------------------------------------------------- Tue Jun 05 10:08:50 CEST 2018 - jgonzalez@suse.com - version 2.8.8.5-1 - adds check for realpath and uses readlink instead (bsc#1088349) ------------------------------------------------------------------- Mon May 21 13:33:05 CEST 2018 - jgonzalez@suse.com - version 2.8.8.4-1 - Decode data before writing data to up2date file (bsc#1093473) ------------------------------------------------------------------- Wed May 16 17:28:34 CEST 2018 - jgonzalez@suse.com - version 2.8.8.3-1 - Show deprecation warning for --salt option in mgr-bootstrap (bsc#1093549) ------------------------------------------------------------------- Mon May 07 15:19:32 CEST 2018 - jgonzalez@suse.com - version 2.8.8.2-1 - fix bootstrap script for python3 (bsc#1091840) ------------------------------------------------------------------- Mon Mar 26 08:49:11 CEST 2018 - jgonzalez@suse.com - version 2.8.8.1-1 - Sync with upstream (bsc#1083294) - Add detection of multiple rhnlib package installs ------------------------------------------------------------------- Mon Mar 05 08:46:53 CET 2018 - jgonzalez@suse.com - version 2.8.7.1-1 - support SLE15 product family - remove empty clean section from spec (bsc#1083294) ------------------------------------------------------------------- Wed Feb 28 09:28:24 CET 2018 - jgonzalez@suse.com - version 2.8.6.1-1 - Clean up SUSE manager repos in bootstrap script for trad clients (bsc#1077997) ------------------------------------------------------------------- Wed Jan 17 11:54:36 CET 2018 - jgonzalez@suse.com - version 2.8.5.1-1 - install push scripts to sbindir - python3 compatibility fixes ------------------------------------------------------------------- Tue Nov 28 12:33:58 CET 2017 - jgonzalez@suse.com - version 2.7.0.8-1 - add comment to explain that FULLY_UPDATE_THIS_BOX gets ignored on salt minions (bsc#1036254) - do not use registration keys from last autoinstallation (bsc#1057599) ------------------------------------------------------------------- Tue Aug 08 11:29:32 CEST 2017 - fkobzik@suse.de - version 2.7.0.7-1 - Improve text for bootstrap (bsc#1032324) ------------------------------------------------------------------- Mon Jun 12 09:04:22 CEST 2017 - mc@suse.de - version 2.7.0.6-1 - fix spelling for certficate - fix html pub path (bsc#1041989) ------------------------------------------------------------------- Mon May 29 15:07:28 CEST 2017 - mc@suse.de - version 2.7.0.5-1 - fix minor typos in bootstrap.sh - update openssl on bootstrap (bsc#1037828) - setup bootstrap repo also when no packages are missing - update important packages before registration (bsc#1037355) ------------------------------------------------------------------- Wed May 03 16:17:34 CEST 2017 - michele.bologna@suse.com - version 2.7.0.4-1 - Always restart the minion regardless of its current state - Add bogus --salt option for backwards compatibility - Invert default behaviour of 'salt' option - correctly honor disabling of SSL in bootstrap script (bsc#1033383) - Exit for non-traditional bootstrap scripts (bsc#1020904) ------------------------------------------------------------------- Fri Mar 31 09:31:47 CEST 2017 - mc@suse.de - version 2.7.0.3-1 - rename mgr-ssh-proxy-force-cmd -> mgr-proxy-ssh-force-cmd - add option to configure only sshd - restrictive ssh options for user mgrsshtunnel - package mgr-proxy-ssh-push-init - extract utility to configure ssh-push keys on a proxy ------------------------------------------------------------------- Tue Mar 07 14:35:51 CET 2017 - mc@suse.de - version 2.7.0.2-1 - Updated links to github in spec files ------------------------------------------------------------------- Wed Jan 11 15:53:29 CET 2017 - michele.bologna@suse.com - version 2.7.0.1-1 - Version 2.7.0 ------------------------------------------------------------------- Fri Dec 16 12:07:43 CET 2016 - michele.bologna@suse.com - version 2.5.1.7-1 - Allow passing multiple GPG keys to mgr-bootstrap (bsc#989905) ------------------------------------------------------------------- Mon Nov 07 11:09:26 CET 2016 - michele.bologna@suse.com - version 2.5.1.6-1 - No final system update when salt management is used (bsc#1006188) - Use https connection for fetching corporate CA if using-ssl is configured (bsc#1005677) ------------------------------------------------------------------- Thu Oct 13 12:49:42 CEST 2016 - mc@suse.de - version 2.5.1.5-1 - install zypp-plugin-spacewalk only with traditional stack - Fix traditional bootstrap on RES (bsc#1004454) ------------------------------------------------------------------- Thu Oct 06 15:16:28 CEST 2016 - mc@suse.de - version 2.5.1.4-1 - Fix traditional bootstrap for RHEL clients (bsc#1003123) - Added the bootstrap repo for RHEL minions when using the bootstrap.sh script (bsc#1001361) - Use init.d where systemd is not available - Salt: do not up2date/remote-mgmt/disable local repo - Only use the first activation key for minions - Add `--salt` option to mgr-bootstrap command to create a bootstrap script which install and configure salt - Terminate registration if hosts aren't found in nsswitch config (bcs#992565) - check only if all required packages are installed (bsc#992987) ------------------------------------------------------------------- Mon Jul 18 14:22:00 CEST 2016 - jrenner@suse.com - version 2.5.1.3-1 - Correctly update the trust store on SLE11 - re-add lost dependency of spacewalk-base-minimal-config to spacewalk-certs- tools (bsc#984418) - Fix mgr-ssh-push-init with proxy and sudo (bsc#982562) ------------------------------------------------------------------- Mon Mar 21 16:38:44 CET 2016 - mc@suse.de - version 2.5.1.2-1 - fix file permissions (bsc#970550) ------------------------------------------------------------------- Wed Mar 09 10:49:09 CET 2016 - mc@suse.de - version 2.5.1.1-1 - do not run certs post script on EL5 systems ------------------------------------------------------------------- Tue Jan 26 13:59:41 CET 2016 - mc@suse.de - version 2.5.0.4-1 - ssh-push: Improvements regarding the clean up - ssh-push: Improve sudo support for simple registrations - ssh-push: Disable relevant services via systemd as well - ssh-push: ssh-rsa finally seems to be no longer required - Allow for a more restrictive sudo configuration (bsc#961521) - Fix script to work with sudo user (bsc#961521) - remove client bootstrap repo after installing needed packages ------------------------------------------------------------------- Mon Nov 30 10:56:28 CET 2015 - mc@suse.de - version 2.5.0.3-1 - remove sm-client-tools from spacewalk-cert-tools package - fix paths to trust dir and update-ca-certificates tool - handle SUSE trust tools and directories correct ------------------------------------------------------------------- Wed Oct 14 09:51:33 CEST 2015 - mc@suse.de - version 2.5.0.2-1 - No longer require to use rsa hostkey for ssh-push ------------------------------------------------------------------- Wed Oct 07 13:53:49 CEST 2015 - mc@suse.de - version 2.5.0.1-1 - bootstrap.sh: install certificate in the right location on SLE-12 ------------------------------------------------------------------- Wed Sep 23 14:58:02 CEST 2015 - mc@suse.de - version 2.1.6.7-1 - FULLY_UPDATE_THIS_BOX defaults to 0 now; add option '--up2date' to mgr-bootstrap to fully update the system after registration - Added sudo support to ssh-push ------------------------------------------------------------------- Mon Jun 22 15:50:57 CEST 2015 - jrenner@suse.de - version 2.1.6.6-1 - Write logfile for mgr-ssh-push-init to correct location (bsc#918082) - rhn-ssl-tool: add arguments to import custom CA file and server key/certificate files ------------------------------------------------------------------- Tue Feb 03 11:48:10 CET 2015 - mc@suse.de - version 2.1.6.5-1 - Do not allow registering a SUSE Manager server at itself (bnc#841731) - Getting rid of Tabs and trailing spaces ------------------------------------------------------------------- Thu Dec 04 13:15:42 CET 2014 - mc@suse.de - version 2.1.6.4-1 - Modify output in case a file is not found - Remove duplicates from authorized_keys2 as well (bsc#885889) ------------------------------------------------------------------- Fri Sep 12 15:44:42 CEST 2014 - mc@suse.de - version 2.1.6.3-1 - bootstrap.sh: when installing cert via rpm, support both curl and wget - bootstrap.sh: fail if both curl and wget are missing - bootstrap.sh: install certificate in the right location on SLE-12 - Fix removal of existing host key entries (bnc#886391) ------------------------------------------------------------------- Thu Mar 27 14:09:29 CET 2014 - fcastelli@suse.com - version 2.1.6.2-1 - bootstrap: disable local yum repos on RHEL systems (bnc#864787) ------------------------------------------------------------------- Fri Feb 07 13:43:23 CET 2014 - mc@suse.de - version 2.1.6.1-1 - create certificates which use sha256 for the signature algorythm - Log stdout and stderr of ssh-copy-id command into LOGFILE - ssh-keygen fails with an error when known_hosts doesn't exist - use package name to require sudo - Updating the copyright years info - Call the new ssh push script from the old one and print deprecation warning - Refactor and cleanup new ssh push init script ------------------------------------------------------------------- Mon Jan 13 09:34:02 CET 2014 - mc@suse.de - version 2.1.5.1-1 - New ssh-push client initialization script - older Proxies don't implement PRODUCT_NAME ------------------------------------------------------------------- Mon Dec 09 16:39:50 CET 2013 - mc@suse.de - version 2.1.4.1-1 - re-include the zypper install section in the bootstrap script - fix duplicate GPG key and CA Cert section - switch to 2.1 ------------------------------------------------------------------- Thu Nov 28 16:16:49 CET 2013 - mc@suse.de - version 1.7.3.10-1 - include fixed version of sm-client-tools (bnc#823813) ------------------------------------------------------------------- Mon Oct 21 17:12:01 CEST 2013 - mc@suse.de - include fixed version of sm-client-tools (bnc#823813) ------------------------------------------------------------------- Wed Jun 12 11:20:01 CEST 2013 - mc@suse.de - version 1.7.3.9-1 - Remove temp files verbosely even in case of error (bnc#818566) - Adding sudo Requires for spacewalk-certs-tools package - The chkconfig command on RHEL does not know the -d switch - simply test if bootstrap repo exists and use it if yes - update sm-client-tools package * Minor refactorings in the code for resource management. * Remove bootstrap repo after failure. (bnc#801666) * /usr/share/rhn/ directory might not exist. (bnc#801662) * added possibility to override SUSE Manager host for tunneling. * added possibility to override rhn.conf with command line. ------------------------------------------------------------------- Thu Apr 04 15:31:27 CEST 2013 - mc@suse.de - version 1.7.3.8-1 - SSH Server Push (client registration) (FATE#312909) ------------------------------------------------------------------- Fri Feb 08 11:18:34 CET 2013 - mc@suse.de - version 1.7.3.7-1 - Actually use https in the bootstrap script as described in the comment ------------------------------------------------------------------- Thu Nov 22 15:52:51 CET 2012 - jrenner@suse.de - version 1.7.3.6-1 - create rpms compatible with RHEL5 - Code 10 product migration requires 'xsltproc' being installed (bnc#789373) - recompile python files (bnc#776356) ------------------------------------------------------------------- Mon Jul 16 15:17:05 CEST 2012 - ug@suse.de - version 1.7.3.5-1 - observe the --set-hostname parameter. ------------------------------------------------------------------- Mon Jun 25 12:33:49 CEST 2012 - mc@suse.de - version 1.7.3.4-1 - put bootstrap tool sm-client-tools.rpm on SUSE Manager Server ------------------------------------------------------------------- Mon May 14 10:51:37 CEST 2012 - mc@suse.de - version 1.7.3.3-1 ------------------------------------------------------------------- Thu May 10 17:32:20 CEST 2012 - ug@suse.de - don't add bootstrap repo on SLES11 SP2 (bnc#760771) ------------------------------------------------------------------- Fri Apr 27 16:52:29 CEST 2012 - mc@suse.de - version 1.7.3.2-1 - Always regenerate server.pem for jabberd. ------------------------------------------------------------------- Wed Mar 21 17:44:52 CET 2012 - mc@suse.de - version 1.7.3.1-1 - Bumping package version ------------------------------------------------------------------- Mon Jan 30 15:50:46 CET 2012 - ma@suse.de - Enforce removal of zmd stack to support OES management (bnc#743955) ------------------------------------------------------------------- Mon Jan 16 15:29:28 CET 2012 - ma@suse.de - Prevent monthly registration on SUSE Manager clients (FATE#312315) ------------------------------------------------------------------- Tue Jan 10 15:34:38 CET 2012 - ma@suse.de - Bootstrap: Offer to disable YAST Automatic Online Update if it is enabled on the client. (bnc#738054) ------------------------------------------------------------------- Fri Dec 9 14:58:15 CET 2011 - ug@suse.de - pimp bootstrap.sh script in combintation with autoyast ------------------------------------------------------------------- Tue Nov 22 10:10:43 CET 2011 - ma@suse.de - Ported from Manager-1.2: - Adapt dependencies to renamed packages (bnc#722052) - bootstrap: make curl/wget print errors (bnc#723670) - Bootstrap: Read ORG_CA_CERT name from CLIENT_OVERRIDES - Bootstrap: Import GPG keys and CERT as soon as possible (bnc#711428) - Do non-interactive zypper refresh. - After registration disable all repositories not provided by SuSE Manager. (bnc#692509) - Adapt bootstrap to new repository naming schema. - Migrate product metadata when bootstrapping SuSE code10 clients. - After registration disable all repositories not provided by SuSE Manager. ------------------------------------------------------------------- Mon Nov 21 19:11:40 CET 2011 - ma@suse.de - Extra code to bootstrap code10 clients and migrate product metadata.. ------------------------------------------------------------------- Thu Nov 3 19:27:49 CET 2011 - ma@suse.de - Adapt dependencies to renamed packages (bnc#722052) ------------------------------------------------------------------- Mon Sep 5 12:46:24 CEST 2011 - mc@suse.de - hostname default during certificate creation should be the FQDN (bnc#703982) ------------------------------------------------------------------- Wed Aug 10 15:01:04 UTC 2011 - kkaempf@novell.com - Fix the initCFG import path (bnc#711518) ------------------------------------------------------------------- Wed Jul 27 11:25:19 CEST 2011 - ma@suse.de - Do non-interactive zypper refresh. ------------------------------------------------------------------- Wed Jul 20 12:59:00 CEST 2011 - mc@suse.de - remove empty else definition ------------------------------------------------------------------- Fri Jul 8 11:06:29 CEST 2011 - ma@suse.de - After registration disable all repositories not provided by SuSE Manager. (bnc#692509) ------------------------------------------------------------------- Tue Apr 26 18:44:11 CEST 2011 - ma@suse.de - Abort if bootstrap.sh has no permission to write to CWD. (bnc#687490) ------------------------------------------------------------------- Thu Mar 31 15:46:52 CEST 2011 - mantel@suse.de - more debranding ------------------------------------------------------------------- Fri Mar 4 14:46:01 CET 2011 - ma@suse.de - Enforce installation of ORG_CA_CERT rpm. ------------------------------------------------------------------- Mon Feb 21 15:00:56 CET 2011 - ma@suse.de - Always c_rehash a changed server CA cert (bnc#673776) ------------------------------------------------------------------- Thu Feb 17 11:49:28 CET 2011 - ma@suse.de - Evaluate rhnreg_ks return value in bootsprap.sh (bnc#671691) - Fix cleanup code in bootstrap.sh (bnc#670283) ------------------------------------------------------------------- Wed Feb 9 18:05:03 CET 2011 - ro@suse.de - do not require rhn-client-tools on rhel-4 ------------------------------------------------------------------- Thu Feb 3 10:50:54 CET 2011 - ma@suse.de - Allow to define more than one ORG_GPG_KEY in bootstrap.sh (bnc #662996) ------------------------------------------------------------------- Sun Jan 30 15:31:40 CET 2011 - mc@suse.de - backport upstrem fixes ------------------------------------------------------------------- Fri Jan 28 12:53:24 CET 2011 - ma@suse.de - Always generate setup code for AllowConfigManagement and AllowRemoteCommands into bootstrap.sh. Otherwise you had to recreate bootstrap.sh if one of the options is turned on later. ------------------------------------------------------------------- Tue Jan 25 17:15:32 CET 2011 - ma@suse.de - Use binary_payload w9.gzdio in gen-rpm.sh as e.g. lzma is not supported on older systems. ------------------------------------------------------------------- Tue Jan 25 13:32:40 CET 2011 - ma@suse.de - Enable rhn_bootstrap generated bootstrap.sh to manage SUSE systems as well. ------------------------------------------------------------------- Tue Jan 18 15:27:18 CET 2011 - mantel@suse.de - add mgr-* symlinks (BNC #660791) ------------------------------------------------------------------- Wed Sep 15 09:17:39 CEST 2010 - mantel@suse.de - Initial release of spacewalk-certs-tools ------------------------------------------------------------------- 07070100000017000081A4000003E80000006400000001662798DF00000043000000000000000000000000000000000000006200000000spacewalk-certs-tools/spacewalk-certs-tools.changes.mc.fix-bootstrap-of-res-with-zypper-installed- fix liberty bootstrapping when zypper is installed (bsc#1222347) 07070100000018000081A4000003E80000006400000001662798DF00000050000000000000000000000000000000000000005600000000spacewalk-certs-tools/spacewalk-certs-tools.changes.welder.fix-slemicro-reboot-method- Apply reboot method changes for transactional systems in the bootstrap script 07070100000019000081A4000003E80000006400000001662798DF000011F2000000000000000000000000000000000000003100000000spacewalk-certs-tools/spacewalk-certs-tools.spec# # spec file for package spacewalk-certs-tools # # Copyright (c) 2024 SUSE LLC # Copyright (c) 2008-2018 Red Hat, Inc. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via https://bugs.opensuse.org/ # # needsbinariesforbuild %if 0%{?suse_version} %global pub_dir /srv/www/htdocs/pub %else %global pub_dir /var/www/html/pub %endif %global pub_bootstrap_dir %{pub_dir}/bootstrap %global rhnroot %{_datadir}/rhn %global __python /usr/bin/python3 Name: spacewalk-certs-tools Summary: Spacewalk SSL Key/Cert Tool License: GPL-2.0-only Group: Applications/Internet Version: 5.0.3 Release: 1 URL: https://github.com/uyuni-project/uyuni Source0: https://github.com/uyuni-project/uyuni/archive/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildArch: noarch Requires(pre): python3-%{name} = %{version}-%{release} Requires: openssl Requires: rpm-build Requires: spacewalk-base-minimal-config Requires: sudo Requires: tar BuildRequires: docbook-utils BuildRequires: make %if 0%{?suse_version} BuildRequires: filesystem Requires: susemanager-build-keys-web %endif Requires(post): python3-uyuni-common-libs Requires(post): python3-rhnlib Requires(post): python3-rpm %description This package contains tools to generate the SSL certificates required by Spacewalk. %package -n python3-%{name} Summary: Spacewalk SSL Key/Cert Tool Group: Applications/Internet Requires: %{name} = %{version}-%{release} Requires: python3-uyuni-common-libs Requires: spacewalk-backend BuildRequires: python3 BuildRequires: python3-rpm-macros %description -n python3-%{name} Python 3 specific files for %{name}. %prep %setup -q %build #nothing to do here %if 0%{?suse_version} # we need to rewrite etc/httpd/conf => etc/apache2 sed -i 's|etc/httpd/conf|etc/apache2|g' rhn_ssl_tool.py sed -i 's|etc/httpd/conf|etc/apache2|g' sslToolConfig.py sed -i 's|etc/httpd/conf|etc/apache2|g' sign.sh sed -i 's|etc/httpd/conf|etc/apache2|g' ssl-howto.txt %endif %install install -d -m 755 $RPM_BUILD_ROOT/%{rhnroot}/certs sed -i '1s|python\b|python3|' rhn-ssl-tool mgr-package-rpm-certificate-osimage rhn-bootstrap make -f Makefile.certs install PREFIX=$RPM_BUILD_ROOT ROOT=%{rhnroot} \ PYTHONPATH=%{python3_sitelib} PYTHONVERSION=%{python3_version} \ MANDIR=%{_mandir} PUB_BOOTSTRAP_DIR=%{pub_bootstrap_dir} ln -s rhn-ssl-tool-%{python3_version} $RPM_BUILD_ROOT%{_bindir}/rhn-ssl-tool ln -s mgr-ssl-cert-setup-%{python3_version} $RPM_BUILD_ROOT%{_bindir}/mgr-ssl-cert-setup ln -s rhn-bootstrap-%{python3_version} $RPM_BUILD_ROOT%{_bindir}/rhn-bootstrap ln -s mgr-ssl-tool.1.gz $RPM_BUILD_ROOT/%{_mandir}/man1/rhn-ssl-tool.1.gz ln -s mgr-bootstrap.1.gz $RPM_BUILD_ROOT/%{_mandir}/man1/rhn-bootstrap.1.gz ln -s rhn-bootstrap $RPM_BUILD_ROOT/%{_bindir}/mgr-bootstrap ln -s rhn-ssl-tool $RPM_BUILD_ROOT/%{_bindir}/mgr-ssl-tool ln -s rhn-sudo-ssl-tool $RPM_BUILD_ROOT/%{_bindir}/mgr-sudo-ssl-tool %if 0%{?suse_version} %py3_compile -O %{buildroot}/%{python3_sitelib} %endif %files %defattr(-,root,root,-) %dir %{rhnroot}/certs %attr(755,root,root) %{rhnroot}/certs/sign.sh %attr(755,root,root) %{rhnroot}/certs/gen-rpm.sh %attr(755,root,root) %{rhnroot}/certs/update-ca-cert-trust.sh %attr(755,root,root) %{_bindir}/rhn-sudo-ssl-tool %{_bindir}/rhn-ssl-tool %{_bindir}/mgr-ssl-cert-setup %{_bindir}/rhn-bootstrap %attr(755,root,root) %{_sbindir}/mgr-package-rpm-certificate-osimage %doc %{_mandir}/man1/rhn-*.1* %doc %{_mandir}/man1/mgr-*.1* %doc ssl-howto-simple.txt ssl-howto.txt %license LICENSE %dir %{rhnroot} %dir %{pub_dir} %dir %{pub_bootstrap_dir} %{_bindir}/mgr-bootstrap %{_bindir}/mgr-ssl-tool %{_bindir}/mgr-sudo-ssl-tool %files -n python3-%{name} %{python3_sitelib}/certs %attr(755,root,root) %{_bindir}/rhn-ssl-tool-%{python3_version} %attr(755,root,root) %{_bindir}/rhn-bootstrap-%{python3_version} %attr(755,root,root) %{_bindir}/mgr-ssl-cert-setup-%{python3_version} %changelog 0707010000001A000081A4000003E80000006400000001662798DF00000807000000000000000000000000000000000000002B00000000spacewalk-certs-tools/ssl-howto-simple.txtSimplistic SSL howto -------------------- A more detailed instructions on how we produce the keys and certificates can be found in ssl-howto.txt. CA private key and CA public certificate (10-ish year exp example): ------------------------------------------------------------------- openssl genrsa -passout pass:PASSWORD -des3 -out ca.key 2048 openssl req -passin pass:PASSWORD -text -new \ -x509 -days 3560 -sha1 -key ca.key -out ca.crt Server-side private key and certificate request: ------------------------------------------------ # use your hostname for the "common name" openssl genrsa -out server.key 2048 openssl req -new -key server.key -out server.csr Server-side public certificate (10-ish year exp example): NOTE: the CA key-pair signs the server.csr --------------------------------------------------------- # use your hostname for the "common name" # NOTE: the "Z" in YYMMDDHHMMSSZ is the letter "Z" mkdir demoCA/ echo "01" > demoCA/serial echo -n > demoCA/index.txt openssl ca -in server.csr -out server.crt -outdir ./ \ -batch -cert ca.crt -keyfile ca.key -startdate YYMMDDHHMMSSZ \ -days 3650 -md sha1 -passin pass:PASSWORD To verify things: ----------------- openssl req -noout -modulus -in server.csr | openssl sha1 openssl rsa -noout -modulus -in server.key | openssl sha1 openssl x509 -noout -modulus -in server.crt | openssl sha1 How to test and SSL connection with the CA certificate: ------------------------------------------------------- For AS 2.1 (or stunnel <v4.0): /usr/sbin/stunnel -r SERVER_HOSTNAME:443 -cf -v 2 -A CA_CERTIFICATE For RHEL 3 (or stunnel >=v4.0): (1) create rhn-stunnel.cfg: CAfile = CA_CERTIFICATE client = yes connect = SERVER_HOSTNAME:443 foreground = yes verify = 2 (2) use stunnel using configuration file: /usr/sbin/stunnel rhn-stunnel.cfg OpenSSL test client (though hard to decypher): openssl s_client -connect host:443 -CAfile CA_CERTIFICATE openssl s_client -connect xmlrpc.rhn.redhat.com:443 -showcerts 2>/dev/null < /dev/null | openssl x509 -dates -noout 0707010000001B000081A4000003E80000006400000001662798DF0000182E000000000000000000000000000000000000002400000000spacewalk-certs-tools/ssl-howto.txtMore expansive description of how we create CA keys/certs and httpd-server keys/cert-reqs/certs: ------------------------------------------------------------------------------ XXX Needs to be updated XXX Create two files from the template below (rhn-ca-openssl.cnf and rhn-server-openssl.cnf). We do this so that we can have differing C/ST/L/O/OU/CN/emailAddress in the "[ req_distinguished_name ]" sections and slightly different "[ usr_cert ]" sections: """ # rhn-*-openssl.cnf #--------------------------------------------------------------------------- # RHN Management {Satellite,Proxy} autogenerated openSSL configuration file. #--------------------------------------------------------------------------- [ ca ] default_ca = CA_default [ CA_default ] default_bits = 2048 x509_extensions = usr_cert database = index.txt serial = serial # how closely we follow policy policy = policy_optional # for the CA policy [ policy_match ] countryName = optional stateOrProvinceName = optional organizationName = match organizationalUnitName = optional commonName = optional emailAddress = optional [ policy_optional ] countryName = optional stateOrProvinceName = optional organizationName = optional organizationalUnitName = optional commonName = optional emailAddress = optional #--------------------------------------------------------------------------- [ req ] default_bits = 2048 distinguished_name = req_distinguished_name prompt = no x509_extensions = usr_cert [ req_distinguished_name ] C = %s ST = %s L = %s O = %s OU = %s CN = %s emailAddress = %s [ usr_cert ] basicConstraints = %s keyUsage = digitalSignature, keyEncipherment, keyCertSign extendedKeyUsage = serverAuth, clientAuth #nsCertType = server # PKIX recommendations harmless if included in all certificates. nsComment = "RHN SSL Tool Generated Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer:always #=========================================================================== """ <config file string substitution notes> NOTE on "[ req_distinguished_name ]" section (all except C are optional): C = country code ST = province name (or state) L = locality (or city) O = org name OU = org unit CN = common name (hostname usually) emailAddress = email address NOTE on "[ usr_cert ]" section for rhn-ca-openssl.cnf: basicConstraints = CA:true keyUsage = digitalSignature, keyEncipherment, keyCertSign NOTE on "[ usr_cert ]" section for rhn-server-openssl.cnf: basicConstraints = CA:false keyUsage = digitalSignature, keyEncipherment </config file string substitution notes> Notes on key and cert names: ---------------------------- CA certificate = RHN-ORG-TRUSTED-SSL-CERT CA private key = RHN-ORG-PRIVATE-SSL-KEY httpd Server Key = server.key httpd Server Cert Request = server.csr httpd Server Cert = server.crt Generate CA: ------------ openssl genrsa -passout pass:PASSWORD -des3 -out RHN-ORG-PRIVATE-SSL-KEY 2048 Generate Public CA Certificate (a self-signed CA certificate): --------------------------------------------------------------- DAYS = 3650 (10 years) openssl req -passin pass:<PASSWORD> -text -config rhn-ca-openssl.cnf -new \ -x509 -days <DAYS> -sha1 -key RHN-ORG-PRIVATE-SSL-KEY \ -out RHN-ORG-TRUSTED-SSL-CERT Generate Server Key: -------------------- openssl genrsa -out server.key 2048 Generate Server Certificate Request: ------------------------------------ openssl req -sha1 -text -config rhn-server-openssl.cnf -new -key server.key \ -out server.csr Generate the Server Certificate (signed by CA): ----------------------------------------------- if serial file does not exist: echo "01" > serial NOTE: the serial number & signature combination of the CA cert and the server.crt should not match (we make every attempt to ensure this). We are not so concerned with serial number/signature matches for multiple server certs (managed by the index.txt file): echo -n > index.txt STARTDATE = 1 week ago (format: YYMMDDHHMMSSZ) DAYS = 365 (1 year) openssl ca -config rhn-ca-openssl.cnf -in server.csr -out server.crt \ -outdir ./ -batch -cert RHN-ORG-TRUSTED-SSL-CERT \ -keyfile RHN-ORG-PRIVATE-SSL-KEY -startdate <STARTDATE> \ -days <DAYS> -md sha1 -policy policy_optional -passin pass:PASSWORD Build the RPMs: --------------- This one is installed on the RHN server (satellite/proxy): rhn-org-httpd-ssl-key-pair<-MACHINENAME>-VERSION-RELEASE: /etc/httpd/conf/ssl.crt/server.crt /etc/httpd/conf/ssl.key/server.key This one is used by XMLRPC clients to talk to RHN servers (satellite/proxy): rhn-org-trusted-ssl-cert-VERSION-RELEASE: /usr/share/rhn/RHN-ORG-TRUSTED-SSL-CERT How to test an SSL connection with a CA cert: --------------------------------------------- For AS 2.1 (or stunnel <v4.0): /usr/sbin/stunnel -r SERVER_HOSTNAME:443 -cf -v 2 -A THE_CA_CERTIFICATEE For RHEL 3 (or stunnel >=v4.0): (1) create rhn-stunnel.cfg: CAfile = CA_CERTIFICATE client = yes connect = SERVER_HOSTNAME:443 foreground = yes verify = 2 (2) use stunnel using configuration file: /usr/sbin/stunnel rhn-stunnel.cfg OpenSSL test client (though hard to decypher): openssl s_client -connect SERVER_HOSTNAME:443 -CAfile THE_CA_CERTIFICATE And to see the remote server's validity window: openssl s_client -connect SERVER_HOSTNAME:443 -showcerts | openssl x509 \ -dates -noout Alternative Server key and cert generation (with a password): ------------------------------------------------------------- (Don't actually do this. This is for reference only) Key: openssl genrsa -passout pass:PASSWORD -des3 -out server.key 2048 Cert req: openssl req -sha1 -passin pass:PASSWORD \ -config rhn-server-openssl.cnf \ -new -key server.key -out server.csr 0707010000001C000081A4000003E80000006400000001662798DF00006601000000000000000000000000000000000000002400000000spacewalk-certs-tools/sslToolCli.py# pylint: disable=missing-module-docstring,invalid-name # # Copyright (c) 2008--2013 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # rhn-ssl-tool command line option module # ## FIXME: the logic here is *WAY* too complicated. Need to simplify -taw ## language imports # pylint: disable-next=unused-import import os import sys ## utitily imports # pylint: disable-next=deprecated-module from optparse import Option, OptionParser, make_option ## local imports from .sslToolLib import ( daysTil18Jan2038, yearsTil18Jan2038, RhnSslToolException, errnoGeneralError, ) from .sslToolConfig import figureDEFS_dirs, figureDEFS_CA, figureDEFS_server from .sslToolConfig import figureDEFS_distinguishing from .sslToolConfig import DEFS, getOption, reInitDEFS # # option lists. # stitched together later to give a known list of commands. # # pylint: disable-next=invalid-name def _getOptionsTree(defs): """passing in the defaults dictionary (which is not static) build the options tree dependent on whats on the commandline """ # pylint: disable-next=invalid-name _optCAKeyPassword = make_option( "-p", "--password", action="store", type="string", help="CA password" ) # pylint: disable-next=invalid-name _optCAKeyPasswordFile = make_option( "--password-file", action="store", type="string", help="file containing the CA password", ) # pylint: disable-next=invalid-name _optCaKey = make_option( "--ca-key", action="store", type="string", # pylint: disable-next=consider-using-f-string help="CA private key filename (default: %s)" % defs["--ca-key"], ) # pylint: disable-next=invalid-name _optCaCert = make_option( "--ca-cert", action="store", type="string", # pylint: disable-next=consider-using-f-string help="CA certificate filename (default: %s)" % defs["--ca-cert"], ) # _optServerKeyPassword = make_option('-p', '--password', action='store', type="string", help='password to generate the web server's SSL private key') # pylint: disable-next=invalid-name _optCertExp = make_option( "--cert-expiration", action="store", type="int", # pylint: disable-next=consider-using-f-string help="expiration of certificate (default: %s days)" % (int(defs["--cert-expiration"])), ) # pylint: disable-next=invalid-name _optServerKey = make_option( "--server-key", action="store", type="string", # pylint: disable-next=consider-using-f-string help="the web server's SSL private key filename (default: %s)" % defs["--server-key"], ) # pylint: disable-next=invalid-name _optServerCertReq = make_option( "--server-cert-req", action="store", type="string", # pylint: disable-next=consider-using-f-string help="location of the web server's SSL certificate request filename (default: %s)" % defs["--server-cert-req"], ) # pylint: disable-next=invalid-name _optServerCert = make_option( "--server-cert", action="store", type="string", # pylint: disable-next=consider-using-f-string help="the web server SSL certificate filename (default: %s)" % defs["--server-cert"], ) # pylint: disable-next=invalid-name _optCaForce = make_option( "-f", "--force", action="store_true", help="forcibly create a new CA SSL private key and/or public certificate", ) # pylint: disable-next=invalid-name _optCaKeyOnly = make_option( "--key-only", action="store_true", help='(rarely used) only generate a CA SSL private key. Review "--gen-ca --key-only --help" for more information.', ) # pylint: disable-next=invalid-name _optCaCertOnly = make_option( "--cert-only", action="store_true", help='(rarely used) only generate a CA SSL public certificate. Review "--gen-ca --cert-only --help" for more information.', ) # pylint: disable-next=invalid-name _optServerKeyOnly = make_option( "--key-only", action="store_true", help="""(rarely used) only generate the web server's SSL private key. Review "--gen-server --key-only --help" for more information.""", ) # pylint: disable-next=invalid-name _optServerCertReqOnly = make_option( "--cert-req-only", action="store_true", help="""(rarely used) only generate the web server's SSL certificate request. Review "--gen-server --cert-req-only --help" for more information.""", ) # pylint: disable-next=invalid-name _optServerCertOnly = make_option( "--cert-only", action="store_true", help="""(rarely used) only generate the web server's SSL certificate. Review "--gen-server --cert-only --help" for more information.""", ) # pylint: disable-next=invalid-name _optCaCertRpm = make_option( "--ca-cert-rpm", action="store", type="string", help="(rarely changed) RPM name that houses the CA SSL public certificate (the base filename, not filename-version-release.noarch.rpm).", ) # pylint: disable-next=invalid-name _optServerRpm = make_option( "--server-rpm", action="store", type="string", help="(rarely changed) RPM name that houses the web server's SSL key set (the base filename, not filename-version-release.noarch.rpm).", ) # pylint: disable-next=invalid-name _optServerTar = make_option( "--server-tar", action="store", type="string", help="(rarely changed) name of tar archive of the web server's SSL key set and CA SSL public certificate that is used solely by the hosted SUSE Manager Proxy installation routines (the base filename, not filename-version-release.tar).", ) # pylint: disable-next=invalid-name _optRpmPackager = make_option( "--rpm-packager", action="store", type="string", help='(rarely used) packager of the generated RPM, such as "RHN Admin <rhn-admin@example.com>".', ) # pylint: disable-next=invalid-name _optRpmVender = make_option( "--rpm-vendor", action="store", type="string", help='(rarely used) vendor of the generated RPM, such as "IS/IT Example Corp.".', ) # pylint: disable-next=invalid-name _optRpmOnly = make_option( "--rpm-only", action="store_true", help='(rarely used) only generate a deployable RPM. (and tar archive if used during the --gen-server step) Review "<baseoption> --rpm-only --help" for more information.', ) # pylint: disable-next=invalid-name _optNoRpm = make_option( "--no-rpm", action="store_true", help="(rarely used) do everything *except* generate an RPM.", ) # pylint: disable-next=invalid-name _optFromCaCert = make_option( "--from-ca-cert", action="store", type="string", help="(for usage with --gen-ca and --rpm-only) Use a custom CA certificate from the given file. Note this doesn't affect the output CA certificate filename (for this use --ca-cert option).", ) # pylint: disable-next=invalid-name _optFromServerKey = make_option( "--from-server-key", action="store", type="string", help="(for usage with --gen-server and --rpm-only) Use a server private SSL key from the given file. Note this doesn't affect the output server key filename (for this use --server-key option).", ) # pylint: disable-next=invalid-name _optFromServerCert = make_option( "--from-server-cert", action="store", type="string", help="(for usage with --gen-server and --rpm-only) Use server public SSL certificate from the given file. Note this doesn't affect the output server certificate filename (for this use --server-cert option).", ) # pylint: disable-next=invalid-name _optSetHostname = make_option( "--set-hostname", action="store", type="string", # pylint: disable-next=consider-using-f-string help="hostname of the web server you are installing the key set on (default: %s)" % repr(defs["--set-hostname"]), ) # pylint: disable-next=invalid-name _optSetCname = make_option( "--set-cname", action="append", type="string", help="cname alias of the web server, can be specified multiple times", ) # pylint: disable-next=invalid-name _buildRpmOptions = [_optRpmPackager, _optRpmVender, _optRpmOnly] # pylint: disable-next=invalid-name _genOptions = [ make_option( "-v", "--verbose", action="count", help='be verbose. Accumulative: -vvv means "be *really* verbose".', ), make_option( "-d", "--dir", action="store", # pylint: disable-next=consider-using-f-string help="build directory (default: %s)" % defs["--dir"], ), make_option("-q", "--quiet", action="store_true", help="be quiet. No output."), ] # pylint: disable-next=invalid-name _genConfOptions = [ make_option( "--set-country", action="store", type="string", # pylint: disable-next=consider-using-f-string help="2 letter country code (default: %s)" % repr(defs["--set-country"]), ), make_option( "--set-state", action="store", type="string", # pylint: disable-next=consider-using-f-string help="state or province (default: %s)" % repr(defs["--set-state"]), ), make_option( "--set-city", action="store", type="string", # pylint: disable-next=consider-using-f-string help="city or locality (default: %s)" % repr(defs["--set-city"]), ), make_option( "--set-org", action="store", type="string", # pylint: disable-next=consider-using-f-string help='organization or company name, such as "Red Hat Inc." (default: %s)' % repr(defs["--set-org"]), ), make_option( "--set-org-unit", action="store", type="string", # pylint: disable-next=consider-using-f-string help='organizational unit, such as "RHN" (default: %s)' % repr(defs["--set-org-unit"]), ), make_option( "--set-email", action="store", type="string", # pylint: disable-next=consider-using-f-string help="email address (default: %s)" % repr(defs["--set-email"]), ), ] # pylint: disable-next=invalid-name _caConfOptions = [ make_option( "--set-common-name", action="store", type="string", # pylint: disable-next=consider-using-f-string help="common name (default: %s)" % repr(defs["--set-common-name"]), ), ] + _genConfOptions # pylint: disable-next=invalid-name _serverConfOptions = [_optSetHostname, _optSetCname] + _genConfOptions # CA generation options # pylint: disable-next=invalid-name _caOptions = [ _optCaForce, _optCAKeyPassword, _optCAKeyPasswordFile, _optCaKey, ] # CA cert generation options # pylint: disable-next=invalid-name _caCertOptions = [ _optCaForce, _optCAKeyPassword, _optCAKeyPasswordFile, _optCaKey, _optCaCert, _optCertExp, ] + _caConfOptions # server key generation options # pylint: disable-next=invalid-name _serverKeyOptions = [ # _optServerKeyPassword, _optServerKey, ] # server cert req generation options # pylint: disable-next=invalid-name _serverCertReqOptions = [ # _optServerKeyPassword, _optServerKey, _optServerCertReq, ] # server cert generation options # pylint: disable-next=invalid-name _serverCertOptions = [ _optCAKeyPassword, _optCAKeyPasswordFile, _optCaCert, _optCaKey, _optServerCertReq, Option( "--startdate", action="store", type="string", default=defs["--startdate"], # pylint: disable-next=consider-using-f-string help="start date for the web server's SSL certificate validity (format: YYMMDDHHMMSSZ - where Z is a letter; default is 1 week ago: %s)" % defs["--startdate"], ), _optServerCert, _optCertExp, ] # SSL key check options # pylint: disable-next=invalid-name _checkOptions = [_optCAKeyPassword, _optCAKeyPasswordFile] # SSL cert check options # = nothing # the base options # pylint: disable-next=invalid-name _optGenCa = make_option( "--gen-ca", action="store_true", help='generate a Certificate Authority (CA) key pair and public RPM. Review "--gen-ca --help" for more information.', ) # pylint: disable-next=invalid-name _optGenServer = make_option( "--gen-server", action="store_true", help="""generate the web server's SSL key set, RPM and tar archive. Review "--gen-server --help" for more information.""", ) # pylint: disable-next=invalid-name _optCheckKey = make_option( "--check-key", action="store_true", help="""Check SSL CA private key's validity and password. Review "--check-key --help" for more information.""", ) # pylint: disable-next=invalid-name _optCheckCert = make_option( "--check-cert", action="store_true", help="""Check SSL CA cert's validity. Review "--check-cert --help" for more information.""", ) # CA build option tree set possibilities # pylint: disable-next=invalid-name _caSet = ( [_optGenCa] + _caOptions + _caCertOptions + _genOptions + [_optCaKeyOnly, _optCaCertOnly] + _buildRpmOptions + [_optCaCertRpm, _optNoRpm] ) # pylint: disable-next=invalid-name _caKeyOnlySet = [_optGenCa] + _caOptions + _genOptions + [_optCaKeyOnly] # pylint: disable-next=invalid-name _caCertOnlySet = ( [_optGenCa] + _caOptions + _caCertOptions + _genOptions + [_optCaCertOnly] ) # pylint: disable-next=invalid-name _caRpmOnlySet = ( [_optGenCa, _optCaKey, _optCaCert] + _buildRpmOptions + [_optFromCaCert] + [_optCaCertRpm] + _genOptions ) # server build option tree set possibilities # pylint: disable-next=invalid-name _serverSet = ( [_optGenServer] + _serverKeyOptions + _serverCertReqOptions + _serverCertOptions + _serverConfOptions + _genOptions + [_optServerKeyOnly, _optServerCertReqOnly, _optServerCertOnly] + _buildRpmOptions + [_optServerRpm, _optServerTar, _optNoRpm] ) # pylint: disable-next=invalid-name _serverKeyOnlySet = ( [_optGenServer] + _serverKeyOptions + _genOptions + [_optServerKeyOnly] ) # pylint: disable-next=invalid-name _serverCertReqOnlySet = ( [_optGenServer] + _serverKeyOptions + _serverCertReqOptions + _serverConfOptions + _genOptions + [_optServerCertReqOnly] ) # pylint: disable-next=invalid-name _serverCertOnlySet = ( [_optGenServer] + _serverCertOptions + _genOptions + [_optServerCertOnly] ) # pylint: disable-next=invalid-name _serverRpmOnlySet = ( [ _optGenServer, _optServerKey, _optServerCertReq, _optServerCert, _optSetHostname, _optSetCname, ] + _buildRpmOptions + [_optFromServerKey, _optFromServerCert] + [_optServerRpm, _optServerTar] + _genOptions ) # CA key check set possibilities # pylint: disable-next=invalid-name _checkKeySet = [_optCheckKey] + _checkOptions + _genOptions # CA cert check set possibilities # pylint: disable-next=invalid-name _checkCertSet = [_optCheckCert] + _genOptions # pylint: disable-next=invalid-name optionsTree = { "--gen-ca": _caSet, "--gen-server": _serverSet, "--check-key": _checkKeySet, "--check-cert": _checkCertSet, } # quick check about the --*-only options # pylint: disable-next=invalid-name _onlyOpts = ["--key-only", "--cert-req-only", "--cert-only", "--rpm-only"] # pylint: disable-next=invalid-name _onlyIntersection = setIntersection(sys.argv, _onlyOpts) if len(_onlyIntersection) > 1: sys.stderr.write( # pylint: disable-next=consider-using-f-string """\ ERROR: cannot use these options in combination: %s\n""" % repr(_onlyIntersection) ) sys.exit(errnoGeneralError) # pylint: disable-next=invalid-name _onlyIntersection = setIntersection(sys.argv, ["--rpm-only", "--no-rpm"]) if len(_onlyIntersection) > 1: sys.stderr.write( # pylint: disable-next=consider-using-f-string """\ ERROR: cannot use these options in combination: %s\n""" % repr(_onlyIntersection) ) sys.exit(errnoGeneralError) if "--key-only" in sys.argv: optionsTree["--gen-ca"] = _caKeyOnlySet optionsTree["--gen-server"] = _serverKeyOnlySet elif "--cert-only" in sys.argv: optionsTree["--gen-ca"] = _caCertOnlySet optionsTree["--gen-server"] = _serverCertOnlySet elif "--cert-req-key-only" in sys.argv: optionsTree["--gen-server"] = _serverCertReqOnlySet elif "--rpm-only" in sys.argv: optionsTree["--gen-ca"] = _caRpmOnlySet optionsTree["--gen-server"] = _serverRpmOnlySet # pylint: disable-next=invalid-name baseOptions = [_optGenCa, _optGenServer, _optCheckKey, _optCheckCert] return optionsTree, baseOptions def unique(s): """make sure a sequence is unique. Using dead simple method (other faster methods assume too much). Returns a list. """ assert type(s) in (type([]), type(()), type("")) n = len(s) if not n: return [] l = [] for item in s: if item not in l: l.append(item) return l # pylint: disable-next=invalid-name def setIntersection(*sets): """return the intersection of 0 or more sequences. a teeny bit recursive. """ n = len(sets) if n <= 1: return unique(sets[0]) # pylint: disable-next=invalid-name setA = unique(sets[0]) # pylint: disable-next=invalid-name setB = setIntersection(*sets[1:]) inter = [] for item in setA: if item in setB: inter.append(item) return inter ## custom usage text # pylint: disable-next=invalid-name _progName = "mgr-ssl-tool" # pylint: disable-next=consider-using-f-string BASE_USAGE = """\ %s [options] step a %s --check-key [sub-options] step b %s --check-cert step 1 %s --gen-ca [sub-options] step 2 %s --gen-server [sub-options] The four options listed above are "base options". For more help about a particular option, just add --help to either one, such as: %s --gen-ca --help If confused, please refer to the man page or other documentation for sample usage.\ """ % tuple( [_progName] * 6 ) OTHER_USAGE = ( # pylint: disable-next=consider-using-f-string """\ %s [options] If confused, please refer to the man page or other documentation for sample usage.\ """ % _progName ) # pylint: disable-next=invalid-name def _getOptionList(defs): """stitch together the commandline given rules set in optionsTree and the grouping logic. """ # pylint: disable-next=invalid-name optionsTree, baseOptions = _getOptionsTree(defs) # pylint: disable-next=invalid-name optionsList = [] usage = OTHER_USAGE # pylint: disable-next=invalid-name argIntersection = setIntersection(sys.argv, list(optionsTree.keys())) if len(argIntersection) == 1: # pylint: disable-next=invalid-name optionsList = optionsTree[argIntersection[0]] # pylint: disable-next=invalid-name optionsList = unique(optionsList) elif len(argIntersection) > 1: # disallow multiple base options on the same commandline sys.stderr.write( # pylint: disable-next=consider-using-f-string """\ ERROR: cannot use these options in combination: %s (%s --help)\n""" % (argIntersection, _progName) ) sys.exit(errnoGeneralError) else: # if *no* base options on he commandline, clear on the list # and tag on a --help # pylint: disable-next=invalid-name optionsList = baseOptions usage = BASE_USAGE if "--help" not in sys.argv: sys.argv.append("--help") return optionsList, usage # pylint: disable-next=invalid-name def optionParse(): """We parse in 3 steps: (1) parse options (2) set the defaults based on any options we override on the commandline - this is nice for things like (what dir we are working in etc). (3) reparse the options with defaults set Reset the default values DEFS given the options found. """ # force certain "first options". Not beautiful but it works. if len(sys.argv) > 1: if sys.argv[1] not in ( "-h", "--help", "--gen-ca", "--gen-server", "--check-key", "--check-cert", ): # first option was not something we understand. Force a base --help del sys.argv[1:] sys.argv.append("--help") if "--gen-ca" in sys.argv: reInitDEFS(1) else: reInitDEFS(0) ## ## STEP 1: preliminarily parse options ## # print 'XXX STEP1' # pylint: disable-next=invalid-name optionList, usage = _getOptionList(DEFS) # pylint: disable-next=invalid-name optionListNoHelp = optionList[:] fake_help = Option("-h", "--help", action="count", help="") optionListNoHelp.append(fake_help) options, args = OptionParser( option_list=optionListNoHelp, add_help_option=0 ).parse_args() ## ## STEP 2: repopulate DEFS dict based on commandline ## and the *-openssl.cnf files ## # print 'XXX STEP2' figureDEFS_dirs(options) # build directory structure figureDEFS_CA(options) # CA key set stuff figureDEFS_server(options) # server key set stuff figureDEFS_distinguishing(options) # distinguishing name stuff ## ## STEP 3: reparse options again only if --help is in the commandline ## so that we can give a --help with correct defaults set. ## # print 'XXX STEP3' if "-h" in sys.argv or "--help" in sys.argv: # DEFS should be mapped with new values now... let's reparse the options. # The correct help text should be presented and all defaults # should be mapped as expected. # pylint: disable-next=invalid-name optionList, usage = _getOptionList(DEFS) options, args = OptionParser(option_list=optionList, usage=usage).parse_args() # we take no extra commandline arguments that are not linked to an option if args: sys.stderr.write( # pylint: disable-next=consider-using-f-string "\nERROR: these arguments make no sense in this " "context (try --help): %s\n" % repr(args) ) sys.exit(errnoGeneralError) return options class CertExpTooShortException(RhnSslToolException): "certificate expiration must be at least 1 day" class CertExpTooLongException(RhnSslToolException): "cert expiration cannot be > 1 year before the 32-bit overflow (in days)" class InvalidCountryCodeException(RhnSslToolException): "invalid country code. Probably != 2 characters in length." # pylint: disable-next=invalid-name def processCommandline(): options = optionParse() # pylint: disable-next=invalid-name _maxDays = daysTil18Jan2038() cert_expiration = getOption(options, "cert_expiration") if cert_expiration: if cert_expiration < 1: raise CertExpTooShortException( "certificate expiration must be at least 1 day" ) if cert_expiration > _maxDays: raise CertExpTooLongException( # pylint: disable-next=consider-using-f-string "certificate expiration cannot exceed %s days " "(~%.2f years)\n" % (int(_maxDays), yearsTil18Jan2038()) ) country = getOption(options, "set_country") if country is not None and (country == "" or len(country) != 2): raise InvalidCountryCodeException( "country code must be exactly two characters, such as 'US'" ) if options.quiet: options.verbose = -1 if not options.verbose: options.verbose = 0 return options # =============================================================================== 0707010000001D000081A4000003E80000006400000001662798DF0000734A000000000000000000000000000000000000002700000000spacewalk-certs-tools/sslToolConfig.py# pylint: disable=missing-module-docstring,invalid-name,anomalous-backslash-in-string # # Copyright (c) 2008--2015 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # rhn-ssl-tool openssl.cnf style file manipulation class # ## FIXME: the logic here is *WAY* too complicated. Need to simplify -taw ## language imports from __future__ import print_function import os import sys import copy import time import random import socket ## local imports from uyuni.common.fileutils import ( cleanupNormPath, rotateFile, rhn_popen, cleanupAbsPath, ) # pylint: disable-next=unused-import from .sslToolLib import getMachineName, daysTil18Jan2038, incSerial, fixSerial from rhn.stringutils import sstr # defaults where we can see them (NOTE: directory is figured at write time) CERT_PATH = "/usr/share/rhn/certs/" BUILD_DIR = cleanupNormPath("./ssl-build", dotYN=1) HOSTNAME = socket.getfqdn() MACHINENAME = getMachineName(HOSTNAME) CA_KEY_NAME = "RHN-ORG-PRIVATE-SSL-KEY" CA_CRT_NAME = "RHN-ORG-TRUSTED-SSL-CERT" CA_CRT_RPM_NAME = CA_CRT_NAME.lower() BASE_SERVER_RPM_NAME = "rhn-org-httpd-ssl-key-pair" BASE_SERVER_TAR_NAME = "rhn-org-httpd-ssl-archive" LEGACY_CA_KEY_NAME = "ca.key" LEGACY_CA_CRT_NAME = "RHNS-CORP-CA-CERT" LEGACY_SERVER_RPM_NAME1 = "rhns-ssl-cert" LEGACY_SERVER_RPM_NAME2 = "rhn-httpd-ssl-key-pair" LEGACY_CA_CERT_RPM_NAME = "rhns-ca-cert" CA_OPENSSL_CNF_NAME = "rhn-ca-openssl.cnf" SERVER_OPENSSL_CNF_NAME = "rhn-server-openssl.cnf" MD = "sha384" CRYPTO = "-aes-256-cbc" # pylint: disable-next=invalid-name def getOption(options, opt): """fetch the value of an options object item without blowing up upon obvious errors """ assert opt.find("-") == -1 if not options: return None if opt in options.__dict__: # print 'XXX opt, options.__dict__[opt]', opt, options.__dict__[opt] return options.__dict__[opt] else: return None # pylint: disable-next=invalid-name def setOption(options, opt, value): """set the value of an options object item without blowing up upon obvious errors """ if not options: return if opt in options.__dict__: options.__dict__[opt] = value # pylint: disable-next=invalid-name def getStartDate_aWeekAgo(): """for SSL cert/key generation, returns now, minus 1 week just in case weird time zone issues get in the way of a working cert/key. format: YYMMDDHHMMSSZ where Z is the capital letter Z """ aweek = 24 * 60 * 60 * 7 return time.strftime("%y%m%d%H%M%S", time.gmtime(time.time() - aweek)) + "Z" _defs = { "--dir": BUILD_DIR, "--ca-key": "RHN-ORG-PRIVATE-SSL-KEY", "--ca-cert": "RHN-ORG-TRUSTED-SSL-CERT", "--cert-expiration": int(daysTil18Jan2038()), "--startdate": getStartDate_aWeekAgo(), "--server-key": "server.key", "--server-cert-req": "server.csr", "--server-cert": "server.crt", "--jabberd-ssl-cert": "server.pem", "--set-country": "US", "--set-common-name": "", # these two will never appear "--set-hostname": HOSTNAME, # at the same time on the CLI "--ca-cert-rpm": CA_CRT_RPM_NAME, "--server-rpm": BASE_SERVER_RPM_NAME + "-" + MACHINENAME, "--server-tar": BASE_SERVER_TAR_NAME + "-" + MACHINENAME, "--rpm-packager": None, "--rpm-vendor": None, } _defsCa = copy.copy(_defs) _defsCa.update( { "--set-state": "", "--set-city": "", "--set-org": "", "--set-org-unit": "", "--set-email": "", } ) _defsServer = copy.copy(_defs) _defsServer.update( { "--set-state": "North Carolina", "--set-city": "Raleigh", "--set-org": "Example Corp. Inc.", "--set-org-unit": "unit", "--set-email": "admin@example.com", } ) DEFS = _defsServer # pylint: disable-next=invalid-name def reInitDEFS(caYN=0): # pylint: disable-next=global-variable-not-assigned global DEFS if caYN: DEFS.update(_defsCa) else: DEFS.update(_defsServer) # pylint: disable-next=invalid-name def figureDEFS_dirs(options): """figure out the directory defaults (after options being at least parsed once). """ # pylint: disable-next=global-variable-not-assigned global DEFS ## fix up the --dir setting DEFS["--dir"] = getOption(options, "dir") or DEFS["--dir"] or "." DEFS["--dir"] = cleanupNormPath(DEFS["--dir"], dotYN=1) ## fix up the --set-hostname and MACHINENAME settings DEFS["--set-hostname"] = ( getOption(options, "set_hostname") or DEFS["--set-hostname"] or socket.gethostname() ) global MACHINENAME MACHINENAME = getMachineName(DEFS["--set-hostname"]) ## remap to options object setOption(options, "dir", DEFS["--dir"]) setOption(options, "set_hostname", DEFS["--set-hostname"]) # pylint: disable-next=invalid-name def figureDEFS_CA(options): """figure out the defaults (after options being at least parsed once) for the CA key-pair(set) variables. """ # pylint: disable-next=global-variable-not-assigned global DEFS if not getOption(options, "ca_key"): # the various default names for CA keys (a hierarchy) for possibility in (CA_KEY_NAME, "ca.key", "cakey.pem"): if os.path.exists(os.path.join(DEFS["--dir"], possibility)): DEFS["--ca-key"] = possibility break DEFS["--ca-key"] = os.path.basename( getOption(options, "ca_key") or DEFS["--ca-key"] ) DEFS["--ca-cert"] = os.path.basename( getOption(options, "ca_cert") or DEFS["--ca-cert"] ) # the various default names for CA keys and certs if not getOption(options, "ca_cert"): if DEFS["--ca-key"] == CA_KEY_NAME: DEFS["--ca-cert"] = CA_CRT_NAME elif DEFS["--ca-key"] == "ca.key": DEFS["--ca-cert"] = "ca.crt" elif DEFS["--ca-key"] == "cakey.pem": DEFS["--ca-cert"] = "cacert.pem" else: DEFS["--ca-cert"] = "ca.crt" DEFS["--cert-expiration"] = getOption(options, "cert_expiration") or int( daysTil18Jan2038() ) DEFS["--ca-cert-rpm"] = getOption(options, "ca_cert_rpm") or CA_CRT_RPM_NAME DEFS["--rpm-packager"] = getOption(options, "rpm_packager") DEFS["--rpm-vendor"] = getOption(options, "rpm_vendor") if "--cert-expiration" in DEFS: # nothing under 1 day or over # days til 18Jan2038 if DEFS["--cert-expiration"] < 1: DEFS["--cert-expiration"] = 1 # pylint: disable-next=invalid-name _maxdays = int(daysTil18Jan2038()) # already rounded if DEFS["--cert-expiration"] > _maxdays: DEFS["--cert-expiration"] = _maxdays # remap to options object setOption(options, "ca_key", DEFS["--ca-key"]) setOption(options, "ca_cert", DEFS["--ca-cert"]) setOption(options, "cert_expiration", DEFS["--cert-expiration"]) setOption(options, "ca_cert_rpm", DEFS["--ca-cert-rpm"]) # pylint: disable-next=invalid-name def figureDEFS_server(options): """figure out the defaults (after options being at least parsed once) for the server key-pair(set) variables. """ # pylint: disable-next=global-variable-not-assigned global DEFS DEFS["--server-key"] = os.path.basename( getOption(options, "server_key") or DEFS["--server-key"] or "server.key" ) DEFS["--server-cert-req"] = os.path.basename( getOption(options, "server_cert_req") or DEFS["--server-cert-req"] or "server.csr" ) DEFS["--server-cert"] = os.path.basename( getOption(options, "server_cert") or DEFS["--server-cert"] or "server.crt" ) DEFS["--cert-expiration"] = getOption(options, "cert_expiration") or int( daysTil18Jan2038() ) # already rounded DEFS["--server-rpm"] = ( getOption(options, "server_rpm") or BASE_SERVER_RPM_NAME + "-" + MACHINENAME ) DEFS["--server-tar"] = ( getOption(options, "server_tar") or BASE_SERVER_TAR_NAME + "-" + MACHINENAME ) DEFS["--rpm-packager"] = getOption(options, "rpm_packager") DEFS["--rpm-vendor"] = getOption(options, "rpm_vendor") if "--cert-expiration" in DEFS: # nothing under 1 day or over # days til 18Jan2038 if DEFS["--cert-expiration"] < 1: DEFS["--cert-expiration"] = 1 # pylint: disable-next=invalid-name _maxdays = int(daysTil18Jan2038()) # already rounded if DEFS["--cert-expiration"] > _maxdays: DEFS["--cert-expiration"] = _maxdays # remap to options object setOption(options, "server_key", DEFS["--server-key"]) setOption(options, "server_cert_req", DEFS["--server-cert-req"]) setOption(options, "server_cert", DEFS["--server-cert"]) setOption(options, "cert_expiration", DEFS["--cert-expiration"]) setOption(options, "server_rpm", DEFS["--server-rpm"]) setOption(options, "server_tar", DEFS["--server-tar"]) # pylint: disable-next=invalid-name def figureDEFS_distinguishing(options): """figure out the defaults (after options being at least parsed once) for the distinguishing variables (C, ST, L, O, OU, CN, emailAddress) First from config file, then from commanline. """ # pylint: disable-next=global-variable-not-assigned global DEFS # if options: # print 'XXX options.__dict__.keys()', options.__dict__.keys() # print 'XXX figureDEFS_distinguishing()' ## map the config file settings to the DEFS object conf = {} # pylint: disable-next=invalid-name caYN = "--gen-ca-cert" in sys.argv or "--gen-ca" in sys.argv if caYN: conf = ConfigFile(os.path.join(DEFS["--dir"], CA_OPENSSL_CNF_NAME)).parse() else: conf = ConfigFile( os.path.join(DEFS["--dir"], MACHINENAME, SERVER_OPENSSL_CNF_NAME) ).parse() mapping = { "C": ("--set-country",), "ST": ("--set-state",), "L": ("--set-city",), "O": ("--set-org",), "OU": ("--set-org-unit",), "CN": ("--set-common-name", "--set-hostname"), #'CN' : ('--set-common-name',), "emailAddress": ("--set-email",), } # map config file settings to DEFS (see mapping dict above) for key in list(conf.keys()): # print 'XXX KEY', key, repr(mapping[key]) for v in mapping[key]: DEFS[v] = conf[key] # print 'XXX DEFS["%s"]' % v, '=', conf[key] ## map commanline options to the DEFS object if getOption(options, "set_country") is not None: DEFS["--set-country"] = getOption(options, "set_country") if getOption(options, "set_state") is not None: DEFS["--set-state"] = getOption(options, "set_state") if getOption(options, "set_city") is not None: DEFS["--set-city"] = getOption(options, "set_city") if getOption(options, "set_org") is not None: DEFS["--set-org"] = getOption(options, "set_org") if getOption(options, "set_org_unit") is not None: DEFS["--set-org-unit"] = getOption(options, "set_org_unit") if getOption(options, "set_common_name") is not None: DEFS["--set-common-name"] = getOption(options, "set_common_name") if getOption(options, "set_hostname") is not None: DEFS["--set-hostname"] = getOption(options, "set_hostname") if getOption(options, "set_email") is not None: DEFS["--set-email"] = getOption(options, "set_email") DEFS["--set-cname"] = getOption(options, "set_cname") # this is list # remap to options object setOption(options, "set_country", DEFS["--set-country"]) setOption(options, "set_state", DEFS["--set-state"]) setOption(options, "set_city", DEFS["--set-city"]) setOption(options, "set_org", DEFS["--set-org"]) setOption(options, "set_org_unit", DEFS["--set-org-unit"]) setOption(options, "set_common_name", DEFS["--set-common-name"]) # setOption(options, 'set_hostname', DEFS['--set-hostname']) setOption(options, "set_email", DEFS["--set-email"]) setOption(options, "set_cname", DEFS["--set-cname"]) CONF_TEMPLATE_CA = """\ # rhn-ca-openssl.cnf #--------------------------------------------------------------------------- # RHN Management {Satellite,Proxy} autogenerated openSSL configuration file. #--------------------------------------------------------------------------- [ ca ] default_ca = CA_default [ CA_default ] default_bits = 4096 x509_extensions = ca_x509_extensions dir = %s database = $dir/index.txt serial = $dir/serial # how closely we follow policy policy = policy_optional copy_extensions = copy [ policy_optional ] countryName = optional stateOrProvinceName = optional organizationName = optional organizationalUnitName = optional commonName = optional emailAddress = optional #--------------------------------------------------------------------------- [ req ] default_bits = 4096 distinguished_name = req_distinguished_name prompt = no x509_extensions = req_ca_x509_extensions [ req_distinguished_name ] %s [ req_ca_x509_extensions ] basicConstraints = CA:true keyUsage = digitalSignature, keyEncipherment, keyCertSign extendedKeyUsage = serverAuth, clientAuth # PKIX recommendations harmless if included in all certificates. nsComment = "RHN SSL Tool Generated Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer:always [ req_server_x509_extensions ] basicConstraints = CA:false keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = serverAuth, clientAuth nsCertType = server # PKIX recommendations harmless if included in all certificates. nsComment = "RHN SSL Tool Generated Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer:always #=========================================================================== """ CONF_TEMPLATE_SERVER = """\ # rhn-server-openssl.cnf #--------------------------------------------------------------------------- # RHN Management {Satellite,Proxy} autogenerated openSSL configuration file. #--------------------------------------------------------------------------- [ req ] default_bits = 4096 distinguished_name = req_distinguished_name prompt = no x509_extensions = req_server_x509_extensions req_extensions = v3_req [ req_distinguished_name ] %s [ req_server_x509_extensions ] basicConstraints = CA:false keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = serverAuth, clientAuth nsCertType = server # PKIX recommendations harmless if included in all certificates. nsComment = "RHN SSL Tool Generated Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer:always [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment # Some CAs do not yet support subjectAltName in CSRs. # Instead the additional names are form entries on web # pages where one requests the certificate... subjectAltName = @alt_names [alt_names] %s #=========================================================================== """ def gen_req_alt_names(d, hostname): """generates the alt_names section of the *-openssl.cnf file""" i = 0 result = "" dnsname = [hostname] if "--set-cname" in d and d["--set-cname"]: dnsname.extend(d["--set-cname"]) for name in dnsname: i += 1 # pylint: disable-next=consider-using-f-string result += "DNS.%d = %s\n" % (i, name) return result def gen_req_distinguished_name(d): """generates the rhn_distinguished section of the *-openssl.cnf file""" s = "" keys = ("C", "ST", "L", "O", "OU", "CN", "emailAddress") for key in keys: if key in d and d[key].strip(): # pylint: disable-next=consider-using-f-string s = s + key + (24 - len(key)) * " " + "= %s\n" % d[key].strip()[:63] else: s = s + "#" + key + (24 - len(key)) * " " + '= ""\n' return s # pylint: disable-next=invalid-name def figureSerial(caCertFilename, serialFilename, indexFilename): """for our purposes we allow the same serial number for server certs BUT WE DO NOT ALLOW server certs and CA certs to share the same serial number. We blow away the index.txt file each time because we are less concerned with matching serials/signatures between server.crt's. """ # what serial # is the ca cert using (we need to increment from that) ret, outstream, errstream = rhn_popen( ["/usr/bin/openssl", "x509", "-noout", "-serial", "-in", caCertFilename] ) out = sstr(outstream.read()) outstream.close() sslerrmsg = ( "non-zero exitcode.\n" "If you ran configure-proxy.sh, try copying again the certs from the SUSE Manager Server\n" f"exit-code: {ret}\n" f"error: {sstr(errstream.read())}\n" ) errstream.close() assert not ret, sslerrmsg # pylint: disable-next=invalid-name caSerial = out.strip().split("=") assert len(caSerial) > 1 # pylint: disable-next=invalid-name caSerial = caSerial[1] # pylint: disable-next=invalid-name,eval-used caSerial = eval("0x" + caSerial) # initialize the serial value (starting at whatever is in # serialFilename or 1) serial = 1 if os.path.exists(serialFilename): # pylint: disable-next=unspecified-encoding serial = open(serialFilename, "r").read().strip() if serial: # pylint: disable-next=eval-used serial = eval("0x" + serial) else: serial = 1 # make sure it is at least 1 more than the CA's serial code always # REMEMBER: openssl will incremented the serial number each time # as well. if serial <= caSerial: random.seed() # pylint: disable-next=eval-used max_serial = eval("0x" + "F" * 40) serial = random.randrange(1, max_serial - caSerial / 2) serial = fixSerial(hex(serial)) # create the serial file if it doesn't exist # write the digits to this file # pylint: disable-next=unspecified-encoding open(serialFilename, "w").write(serial + "\n") os.chmod(serialFilename, int("0600", 8)) # truncate the index.txt file. Less likely to have unneccessary clashes. # pylint: disable-next=unspecified-encoding open(indexFilename, "w") os.chmod(indexFilename, int("0600", 8)) return serial # pylint: disable-next=missing-class-docstring class ConfigFile: def __init__(self, filename=None): self.filename = filename if self.filename is None: self.filename = SERVER_OPENSSL_CNF_NAME if os.path.exists(os.path.join(DEFS["--dir"], "rhn_openssl.cnf")): self.filename = os.path.join(DEFS["--dir"], "rhn_openssl.cnf") elif os.path.exists(os.path.join(DEFS["--dir"], "openssl.cnf")): self.filename = os.path.join(DEFS["--dir"], "openssl.cnf") self.filename = cleanupAbsPath(self.filename) def parse(self): """yank all the pertinent ssl data from a previously generated openssl.cnf. NOTE: we get a limited sampling of info here. We have no concept of the [ some heading ] divisions in the rhn_openssl.cnf file. """ d = {} try: # pylint: disable-next=unspecified-encoding fo = open(self.filename, "r") # pylint: disable-next=bare-except except: return d line = fo.readline() while line: if line.strip() == "[ req_distinguished_name ]": break line = fo.readline() # genKeys = ['dir'] # caKeys = ['private_key', 'certificate',] keys = [ "C", "ST", "L", "O", "OU", "CN", "emailAddress", ] # ] + caKeys + genKeys for s in fo.readlines(): s = s.strip() if len(s) > 2 and s[0] == "[" and s[-1] == "]": break split = s.split() if not split or len(split) < 3: continue if split[0] not in keys: continue split = s.split("=") if len(split) != 2: continue for i in range(len(split)): split[i] = split[i].strip() d[split[0]] = split[1] return d # pylint: disable-next=invalid-name def updateLegacy(self, newdir=None, verbosity=1): """in slightly older formatted ca_openssl.cnf files, there was no dir setting seperate from the database and serial settings. This function fixes that setup. Most of the time this function short-circuits early. """ try: # pylint: disable-next=unspecified-encoding fo = open(self.filename, "r") # pylint: disable-next=bare-except except: return if newdir is None: newdir = os.path.dirname(self.filename) newfile = "" # pylint: disable-next=invalid-name in_CA_defaultYN = 0 # pylint: disable-next=invalid-name dirSetYN = 0 line = fo.readline() while line: # pylint: disable-next=invalid-name cleanLine = line.strip() # is this a label? # pylint: disable-next=invalid-name isLabelYN = 0 if cleanLine and (cleanLine[0], cleanLine[-1]) == ("[", "]"): # pylint: disable-next=invalid-name isLabelYN = 1 if cleanLine == "[ CA_default ]": # we don't care much until we hit this label # pylint: disable-next=invalid-name in_CA_defaultYN = 1 elif isLabelYN: # pylint: disable-next=invalid-name in_CA_defaultYN = 0 # hit another label if in_CA_defaultYN: vector = line.split("=") if len(vector) == 2: key = vector[0].strip() if key == "dir": # we should be OK - short-circuit return if key in ("database", "serial"): # we never hit a "dir" key if not dirSetYN: newfile = ( newfile # pylint: disable-next=consider-using-f-string + """\ dir = %s database = $dir/index.txt serial = $dir/serial """ % newdir ) # pylint: disable-next=invalid-name dirSetYN = 1 line = fo.readline() continue newfile = newfile + line line = fo.readline() try: rotated = rotateFile(filepath=self.filename, verbosity=verbosity) if verbosity >= 0 and rotated: print( # pylint: disable-next=consider-using-f-string "Rotated: %s --> %s" % (os.path.basename(self.filename), os.path.basename(rotated)) ) except ValueError: pass # pylint: disable-next=unspecified-encoding fo = open(self.filename, "w") fo.write(newfile) fo.close() os.chmod(self.filename, int("0600", 8)) return dirSetYN # pylint: disable-next=invalid-name def updateDir(self, newdir=None, verbosity=0): """changes the CA configuration file's directory setting (if need be) in place. Touches nothing else. """ if self.updateLegacy(newdir): return try: # pylint: disable-next=unspecified-encoding fo = open(self.filename, "r") # pylint: disable-next=bare-except except: return olddir = "" if newdir is None: newdir = os.path.dirname(self.filename) newfile = "" # pylint: disable-next=invalid-name hit_CA_defaultYN = 0 line = fo.readline() while line: if line.strip() == "[ CA_default ]": # we don't care much until we hit this label # pylint: disable-next=invalid-name hit_CA_defaultYN = 1 if hit_CA_defaultYN: vector = line.split("=") if len(vector) == 2: key, value = vector if key.strip() == "dir": value = value.strip() olddir = value # pylint: disable-next=consider-using-f-string line = "%s= %s\n" % (key, newdir) # pylint: disable-next=invalid-name hit_CA_defaultYN = 0 if newdir == olddir: # nothing to do return newfile = newfile + line line = fo.readline() try: rotated = rotateFile(filepath=self.filename, verbosity=verbosity) if verbosity >= 0 and rotated: print( # pylint: disable-next=consider-using-f-string "Rotated: %s --> %s" % (os.path.basename(self.filename), os.path.basename(rotated)) ) except ValueError: pass # pylint: disable-next=unspecified-encoding fo = open(self.filename, "w") fo.write(newfile) fo.close() os.chmod(self.filename, int("0600", 8)) # pylint: disable-next=invalid-name def save(self, d, caYN=0, verbosity=0): """d == commandline dictionary""" mapping = { "--set-country": "C", "--set-state": "ST", "--set-city": "L", "--set-org": "O", "--set-org-unit": "OU", "--set-common-name": "CN", # these two will never occur at the "--set-hostname": "CN", # same time "--set-email": "emailAddress", } rdn = {} for k in list(d.keys()): if k in mapping: rdn[mapping[k]] = d[k].strip() openssl_cnf = "" if caYN: openssl_cnf = CONF_TEMPLATE_CA % ( os.path.dirname(self.filename) + "/", gen_req_distinguished_name(rdn), ) else: openssl_cnf = CONF_TEMPLATE_SERVER % ( gen_req_distinguished_name(rdn), gen_req_alt_names(d, rdn["CN"]), ) try: rotated = rotateFile(filepath=self.filename, verbosity=verbosity) if verbosity >= 0 and rotated: print( # pylint: disable-next=consider-using-f-string "Rotated: %s --> %s" % (os.path.basename(self.filename), os.path.basename(rotated)) ) except ValueError: pass # pylint: disable-next=unspecified-encoding fo = open(self.filename, "w") fo.write(openssl_cnf) fo.close() os.chmod(self.filename, int("0600", 8)) return openssl_cnf ## ## generated RPM "configuration" dumping ground: ## POST_UNINSTALL_SCRIPT = """\ if [ \$1 = 0 ]; then # The following steps are copied from mod_ssl's postinstall scriptlet # Make sure the permissions are okay umask 077 if [ ! -f /etc/httpd/conf/ssl.key/server.key ] ; then /usr/bin/openssl genrsa -rand /proc/apm:/proc/cpuinfo:/proc/dma:/proc/filesystems:/proc/interrupts:/proc/ioports:/proc/pci:/proc/rtc:/proc/uptime 1024 > /etc/httpd/conf/ssl.key/server.key 2> /dev/null fi if [ ! -f /etc/httpd/conf/ssl.crt/server.crt ] ; then cat << EOF | /usr/bin/openssl req -new -key /etc/httpd/conf/ssl.key/server.key -x509 -days 365 -out /etc/httpd/conf/ssl.crt/server.crt 2>/dev/null -- SomeState SomeCity SomeOrganization SomeOrganizationalUnit localhost.localdomain root@localhost.localdomain EOF fi /sbin/service httpd graceful || /sbin/service httpd try-restart exit 0 fi """ SERVER_RPM_SUMMARY = "Organizational server (httpd) SSL key-pair/key-set." CA_CERT_RPM_SUMMARY = "Organizational public SSL CA certificate " "(client-side)." # =============================================================================== 0707010000001E000081A4000003E80000006400000001662798DF000010D6000000000000000000000000000000000000002400000000spacewalk-certs-tools/sslToolLib.py# pylint: disable=missing-module-docstring,invalid-name # # Copyright (c) 2008--2012 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # rhn-ssl-tool general library # ## language imports import os import sys import shutil import tempfile from .timeLib import DAY, now, secs2days, secs2years class RhnSslToolException(Exception): """general exception class for the tool""" # pylint: disable-next=invalid-name errnoGeneralError = 1 # pylint: disable-next=invalid-name errnoSuccess = 0 # pylint: disable-next=invalid-name def fixSerial(serial): """fixes a serial number this may be wrongly formatted""" if not serial: serial = "00" if serial.find("0x") == -1: serial = "0x" + serial # strip the '0x' if present serial = serial.split("x")[-1] # the string might have a trailing L serial = serial.replace("L", "") # make sure the padding is correct # if odd number of digits, pad with a 0 # e.g., '100' --> '0100' if len(serial) / 2.0 != len(serial) // 2: serial = "0" + serial return serial # pylint: disable-next=invalid-name def incSerial(serial): """increment a serial hex number""" if not serial: serial = "00" if serial.find("0x") == -1: serial = "0x" + serial # pylint: disable-next=eval-used serial = eval(serial) + 1 serial = hex(serial) serial = serial.split("x")[-1] return fixSerial(serial) # pylint: disable-next=invalid-name def getMachineName(hostname): """xxx.yyy.zzz.com --> xxx.yyy yyy.zzz.com --> yyy zzz.com --> zzz.com xxx --> xxx *.yyy.zzz.com --> _star_.yyy """ hn = hostname.replace("*", "_star_").split(".") if len(hn) < 3: return hostname return ".".join(hn[:-2]) # # NOTE: the Unix epoch overflows at: 2038-01-19 03:14:07 (2^31 seconds) # # pylint: disable-next=invalid-name def secsTil18Jan2038(): """(int) secs til 1 day before the great 32-bit overflow We are making it 1 day just to be safe. """ return 2147483647 - now() - DAY # pylint: disable-next=invalid-name def daysTil18Jan2038(): "(float) days til 1 day before the great 32-bit overflow" return secs2days(secsTil18Jan2038()) # pylint: disable-next=invalid-name def yearsTil18Jan2038(): "(float) approximate years til 1 day before the great 32-bit overflow" return secs2years(secsTil18Jan2038()) def gendir(directory): "makedirs, but only if it doesn't exist first" if not os.path.exists(directory): try: os.makedirs(directory, int("0700", 8)) except OSError as e: # pylint: disable-next=consider-using-f-string print("Error: %s" % (e,)) sys.exit(1) def chdir(newdir): "chdir with the previous cwd as the return value" cwd = os.getcwd() os.chdir(newdir) return cwd class TempDir: """temp directory class with a cleanup destructor and method""" _shutil = shutil # trying to hang onto shutil during garbage collection def __init__(self, suffix="-rhn-ssl-tool"): "create a temporary directory in /tmp" if suffix.find("/") != -1: raise ValueError("suffix cannot be a path, only a name") # add some quick and dirty randomness to the tempfilename s = "" while len(s) < 10: s = s + str(ord(os.urandom(1))) self.path = tempfile.mkdtemp(suffix="-" + s + suffix) def getdir(self): return self.path getpath = getdir def __del__(self): """delete temporary directory when done with it""" self._shutil.rmtree(self.path) close = __del__ # =============================================================================== 0707010000001F000081A4000003E80000006400000001662798DF00000970000000000000000000000000000000000000002100000000spacewalk-certs-tools/timeLib.py# pylint: disable=missing-module-docstring,invalid-name # # Copyright (c) 2008--2013 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # # consistent (predictable) time functions # # Author: taw@redhat.com # # NOTE: translating seconds to years is an approximation. I.e., xxx secs * # 365 not really right. Some years do not come out to 365 days. # # # The Unix Epoch is/was: 1970-01-01 00:00:00 # # NOTE: the POSIX time range is defined (for 32 bit machines) as: # seconds: -2147483648L - 2147483647L # readable: 1901-12-13 20:45:52 - 2038-01-19 03:14:07 # tuples: (1901, 12, 13, 20, 45, 52, 4, 347, 0) # to (2038, 1, 19, 3, 14, 7, 1, 19, 0) # from __future__ import print_function from time import strftime, strptime, mktime, gmtime, timezone from time import time MIN = 60.0 HOUR = 60 * MIN DAY = 24 * HOUR WEEK = 7 * DAY YEAR = 365 * DAY def now(): return round(time()) # pylint: disable-next=redefined-builtin def secs2str(format, secs): assert type(secs) in (type(1), type(1.0)) return strftime(format, gmtime(round(secs))) # pylint: disable-next=redefined-builtin def str2secs(s, format): return mktime(strptime(s, format)) - timezone def secs2days(secs): return round(secs / DAY) def secs2years(secs): "an approximation" return round(secs / YEAR) # ----------------------------------------------------------------------------- def _test(): # pylint: disable-next=invalid-name nowS = now() # pylint: disable-next=invalid-name F = "%b %d %H:%M:%S %Y" print("Right now, in seconds (epoch): ", nowS) print("Right now, stringified: ", secs2str(F, nowS)) print("YEAR, WEEK, DAY, HOUR, MIN: ", YEAR, WEEK, DAY, HOUR, MIN) print("secs2days(DAY): ", secs2days(DAY)) print("secs2years(YEAR):", secs2years(YEAR)) if __name__ == "__main__": _test() 07070100000020000081A4000003E80000006400000001662798DF00000C6A000000000000000000000000000000000000002E00000000spacewalk-certs-tools/update-ca-cert-trust.sh# Copyright (c) 2008--2016 Red Hat, Inc. # # This software is licensed to you under the GNU General Public License, # version 2 (GPLv2). There is NO WARRANTY for this software, express or # implied, including the implied warranties of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 # along with this software; if not, see # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. # # Red Hat trademarks are not licensed under GPLv2. No permission is # granted to use or replicate Red Hat trademarks that are incorporated # in this software or its documentation. # # Scripts that adds/removes RHN-ORG-TRUSTED-SSL-CERT into/from system-wide # trusted certificates. # The script checks if RHN-ORG-TRUSTED-SSL-CERT is present # in /usr/share/rhn and HTTP dir. # The assumption: CA in HTTP dir is an own created CA and we are a Server or Proxy # This CA is copyied to the trust dir under the name LOCAL-RHN-ORG-TRUSTED-SSL-CERT # If there is a CA in /usr/share/rhn it is expected to be the CA deployed by a # registration. It is copied into the trust dir as RHN-ORG-TRUSTED-SSL-CERT. # If the client is registered using salt, /usr/share/rhn might be empty the the state # is copying the CA directly to the trust dir as RHN-ORG-TRUSTED-SSL-CERT. # Finally the trust update is run. # # Optional argument: Certificate file name CERT_DIR=/usr/share/rhn CA_NAME="RHN-ORG-TRUSTED-SSL-CERT" LOCAL_CA_NAME="LOCAL-RHN-ORG-TRUSTED-SSL-CERT" if [ -n "$1" -a -f "$CERT_DIR/$1" ]; then CERT_FILE=$1 else CERT_FILE=$CA_NAME fi CA_HTTP_DIR=/var/www/html/pub/ TRUST_DIR=/etc/pki/ca-trust/source/anchors UPDATE_TRUST_CMD="/usr/bin/update-ca-trust extract" if [ -d /etc/pki/ca-trust/source/anchors -a -x /usr/bin/update-ca-trust ]; then TRUST_DIR=/etc/pki/ca-trust/source/anchors elif [ -d /etc/pki/trust/anchors/ -a -x /usr/sbin/update-ca-certificates ]; then # SLE 12+ TRUST_DIR=/etc/pki/trust/anchors UPDATE_TRUST_CMD="/usr/sbin/update-ca-certificates" CA_HTTP_DIR=/srv/www/htdocs/pub/ elif [ -d /etc/ssl/certs -a -x /usr/bin/c_rehash ]; then # SLE 11 TRUST_DIR=/etc/ssl/certs UPDATE_TRUST_CMD="/usr/bin/c_rehash" rm -f $TRUST_DIR/RHN-ORG-TRUSTED-SSL-CERT.pem rm -f $TRUST_DIR/RHN-ORG-TRUSTED-SSL-CERT-*.pem if [ -f $CERT_DIR/$CERT_FILE ]; then ln -sf $CERT_DIR/$CERT_FILE $TRUST_DIR/RHN-ORG-TRUSTED-SSL-CERT.pem if [ $(grep -- "-----BEGIN CERTIFICATE-----" $CERT_DIR/$CERT_FILE | wc -l) -gt 1 ]; then csplit -b "%02d.pem" -f $TRUST_DIR/RHN-ORG-TRUSTED-SSL-CERT- $CERT_DIR/$CERT_FILE '/-----BEGIN CERTIFICATE-----/' '{*}' fi fi $UPDATE_TRUST_CMD >/dev/null exit 0 fi # Not on EL5 if [ ! -d $TRUST_DIR ]; then exit 0 fi if [ -f $CA_HTTP_DIR/$CA_NAME ]; then test ! -f $CERT_DIR/$CERT_FILE || ! cmp -s $CERT_DIR/$CERT_FILE $CA_HTTP_DIR/$CA_NAME && { # this CA will be copied in the next step; we don't need it twice cp $CA_HTTP_DIR/$CA_NAME $TRUST_DIR/$LOCAL_CA_NAME } else rm -f $TRUST_DIR/$LOCAL_CA_NAME fi if [ -f $CERT_DIR/$CERT_FILE ]; then cp $CERT_DIR/$CERT_FILE $TRUST_DIR/$CERT_FILE fi $UPDATE_TRUST_CMD 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor