File spacewalk-certs-tools-git-1253.f8a8a70.obscpio of Package spacewalk-certs-tools
07070100000000000041FD00000000000000000000000268F09C4700000000000000000000000000000000000000000000001600000000spacewalk-certs-tools07070100000001000081B400000000000000000000000168F09C47000046AC000000000000000000000000000000000000001E00000000spacewalk-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.
07070100000002000081B400000000000000000000000168F09C47000007F3000000000000000000000000000000000000002500000000spacewalk-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.*
07070100000003000081B400000000000000000000000168F09C4700000995000000000000000000000000000000000000002400000000spacewalk-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
07070100000004000081B400000000000000000000000168F09C470000033E000000000000000000000000000000000000002600000000spacewalk-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
07070100000005000081B400000000000000000000000168F09C47000002AD000000000000000000000000000000000000002200000000spacewalk-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__ = []
07070100000006000081FD00000000000000000000000168F09C4700002A30000000000000000000000000000000000000002100000000spacewalk-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
cp $RPM_BUILD_DIR/$ARCH/$NAME-$VERSION-$RELEASE.$ARCH.rpm .
cp $RPM_BUILD_DIR/$NAME-$VERSION-$RELEASE.src.rpm .
rm -rf $RPM_BUILD_DIR
07070100000007000081FD00000000000000000000000168F09C4700001DC0000000000000000000000000000000000000002D00000000spacewalk-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="server"
SRVALTNAME="DNS:server.fqdn"
DBCRT="db"
DBALTNAME="DNS:db,DNS:reportdb,$SRVALTNAME"
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
subjectKeyIdentifier = hash
keyUsage = digitalSignature, keyEncipherment, keyCertSign
extendedKeyUsage = serverAuth, clientAuth
# PKIX recommendations harmless if included in all certificates.
nsComment = "SSL Generated Certificate"
authorityKeyIdentifier = keyid, issuer:always
[ req_server_csr_x509_extensions ]
basicConstraints = CA:false
subjectKeyIdentifier = hash
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
nsCertType = server
# PKIX recommendations harmless if included in all certificates.
nsComment = "SSL Generated Certificate"
subjectAltName = \${ENV::subaltname}
[ 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_csr_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
export commonname=$DBCRT
export subaltname=$DBALTNAME
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_csr_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/$DBCRT.crt $DIR/package/db.crt
if [ $PKEYALGO = "rsa" ]; then
openssl rsa -passin pass:$PASSWORD -in $DIR/private/$DBCRT.key -out $DIR/package/db.key
else
openssl ec -passin pass:$PASSWORD -in $DIR/private/$DBCRT.key -out $DIR/package/db.key
fi
echo "Test Certificates in $DIR/package/"
07070100000008000081B400000000000000000000000168F09C47000018D7000000000000000000000000000000000000002900000000spacewalk-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>--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="" </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>
07070100000009000081B400000000000000000000000168F09C47000012AD000000000000000000000000000000000000003A00000000spacewalk-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()
#===============================================================================
0707010000000A000081B400000000000000000000000168F09C4700000C09000000000000000000000000000000000000003F00000000spacewalk-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>
0707010000000B000081FD00000000000000000000000168F09C4700000571000000000000000000000000000000000000002900000000spacewalk-certs-tools/mgr-ssl-cert-setup#!/usr/bin/python3
# pylint: disable=missing-module-docstring,invalid-name
#
# 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
# pylint: disable-next=unused-import
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
# ===============================================================================
0707010000000C000081B400000000000000000000000168F09C470000665C000000000000000000000000000000000000002800000000spacewalk-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>
</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>
</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-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>-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-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>
0707010000000D000081FD00000000000000000000000168F09C470000616A000000000000000000000000000000000000002C00000000spacewalk-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
import re
# 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
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)
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("--show-container-setup", action="store_true")
parser.add_argument("--verbose", "-v", action="count", default=0)
parser.add_argument("--skip-db", action="store_true")
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
):
intermediate_cas = []
clist = readSplitCertificates(root_ca_file)
root_ca = clist[0]
intermediate_cas.extend(clist[1:])
clist = readSplitCertificates(server_cert_file)
server_cert = clist[0]
intermediate_cas.extend(clist[1:])
for ica in intermediate_ca_files:
clist = readSplitCertificates(ica)
intermediate_cas.extend(clist)
server_key = ""
with open(server_key_file, "r", encoding="utf-8") as f:
server_key = f.read()
return FilesContent(
root_ca=root_ca,
server_cert=server_cert,
server_key=server_key,
intermediate_cas=intermediate_cas,
)
# pylint: disable-next=invalid-name
def readSplitCertificates(certfile):
# pylint: disable-next=invalid-name
isContent = False
cert_list = []
cert = ""
with open(certfile, "r", encoding="utf-8") as f:
certs_content = f.read()
for line in certs_content.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"):
cert_list.append(cert)
# pylint: disable-next=invalid-name
isContent = False
return cert_list
# 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
content = [root_ca_content]
content.extend(intermediate_ca_content)
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 nextval == "authorityKeyIdentifier" and re.match(
r"^\s+[0-9A-Fa-f]{2}:.+$", line
):
data["authorityKeyIdentifier"] = line.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", encoding="utf-8") 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", encoding="utf-8") 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 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
"""
checkCompleteCAChain(server_cert_content, certData)
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)
# 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()
if options.show_container_setup:
container_setup = getContainersSetup(
options.root_ca_file,
options.intermediate_ca_file,
options.server_cert_file,
options.server_key_file,
)
sys.stdout.write(container_setup)
sys.exit(0)
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)
deployCAUyuni(certData)
if not options.skip_db:
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))
# ===============================================================================
0707010000000E000081FD00000000000000000000000168F09C4700000782000000000000000000000000000000000000002400000000spacewalk-certs-tools/rhn-bootstrap#!/usr/bin/python -u
# pylint: disable=missing-module-docstring,invalid-name
#
# 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:
# pylint: disable-next=consider-using-f-string
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
# ===============================================================================
0707010000000F000081FD00000000000000000000000168F09C4700000814000000000000000000000000000000000000002300000000spacewalk-certs-tools/rhn-ssl-tool#!/usr/bin/python
# pylint: disable=missing-module-docstring,invalid-name
#
# 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:
# pylint: disable-next=consider-using-f-string
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
# ===============================================================================
07070100000010000081B400000000000000000000000168F09C4700000072000000000000000000000000000000000000002700000000spacewalk-certs-tools/rhn-stunnel.confCAfile = /usr/share/rhn/RHNS-CA-CERT
client = yes
connect = xmlrpc.rhn.redhat.com:443
foreground = yes
verify = 2
07070100000011000081FD00000000000000000000000168F09C4700000037000000000000000000000000000000000000002800000000spacewalk-certs-tools/rhn-sudo-ssl-tool#!/bin/bash
/usr/bin/sudo /usr/bin/rhn-ssl-tool "$@"
07070100000012000081FD00000000000000000000000168F09C470000754D000000000000000000000000000000000000002700000000spacewalk-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 Multi-Linux 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": "",
"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(
# pylint: disable-next=possibly-used-before-assignment
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)
07070100000013000081B400000000000000000000000168F09C4700009A94000000000000000000000000000000000000002F00000000spacewalk-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 spacewalk.common.rhnConfig import isUyuni, cfg_component
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.
# You can set ACTIVATION_KEYS and ORG_GPG_KEY as environment variables, or they will default to the templated values.
ACTIVATION_KEYS=${{ACTIVATION_KEYS:-{activation_keys}}}
ORG_GPG_KEY=${{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}
#If the environment variable MGR_SERVER_HOSTNAME is set, its value will be used.
#Otherwise, or they will default to the templated value.
HOSTNAME=${{MGR_SERVER_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. PROFILE_NAME="foo.example.com" # For specific client system
# PROFILE_NAME=`hostname -s` # Short hostname
# PROFILE_NAME=`hostname -f` # FQDN
PROFILE_NAME="" # Empty by default to let it be set automatically.
# SUSE Multi-Linux 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 Multi-Linux 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 Multi-Linux 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 Multi-Linux Manager server are the same."
echo "Do not try to register a SUSE Multi-Linux 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 MULTI-LINUX 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
if [ "$BASE" == "" ]; then
grep -q 'cpe:/o:suse:' /etc/os-release && BASE='sl'
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"
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"
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"
PREV_MASTER="$(sed -n 's/^master: //p' $SUSEMANAGER_MASTER_FILE 2> /dev/null)"
# Remove old minion keys so reregistration do different master works
# Delete the pki config only in case of changing the master
if [ -d "$MINION_PKI_CONF" -a "$HOSTNAME" != "$PREV_MASTER" ]; then
echo "* removing old Salt PKI files"
rm -r "$MINION_PKI_CONF"
fi
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
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
}
"""
07070100000014000081FD00000000000000000000000168F09C470000B718000000000000000000000000000000000000002600000000spacewalk-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 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,
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
# 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 Multi-Linux 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 Multi-Linux 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 Multi-Linux 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"])
dependencyCheck(ca_cert)
dependencyCheck(server_key)
dependencyCheck(server_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"])),
]
# 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 Multi-Linux Manager Proxy installation process against SUSE Multi-Linux 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 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)
else:
genServer_dependencies(getCAPassword(options, confirmYN=0), DEFS)
genServerKey(DEFS, options.verbose)
genServerCertReq(DEFS, options.verbose)
genServerCert(getCAPassword(options, confirmYN=0), 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 Multi-Linux 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))
# ===============================================================================
07070100000015000081FD00000000000000000000000168F09C4700002287000000000000000000000000000000000000001E00000000spacewalk-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
07070100000016000081B400000000000000000000000168F09C4700008D7A000000000000000000000000000000000000003400000000spacewalk-certs-tools/spacewalk-certs-tools.changes-------------------------------------------------------------------
Tue Jun 17 19:29:05 CEST 2025 - marina.latini@suse.com
- version 5.1.6-0
* Add the possibility to use env variables for ACTIVATION_KEYS
and ORG_GPG_KEY
* Add the possibility to use env variable for HOSTNAME
-------------------------------------------------------------------
Tue May 13 00:21:53 CEST 2025 - marina.latini@suse.com
- version 5.1.5-0
* Remove rhn-ssl-tool --gen-server RPM feature and options
(bsc#1235696)
-------------------------------------------------------------------
Fri Apr 11 17:32:00 CEST 2025 - marina.latini@suse.com
- version 5.1.4-0
* Remove unneeded SSL certificate deployment for PostgreSQL
* Use the new product name SUSE Multi-Linux Manager
-------------------------------------------------------------------
Thu Mar 27 07:35:23 CET 2025 - marina.latini@suse.com
- version 5.1.3-0
* Enhance mgr-ssl-cert-tool to allow generating container setup
from CLI (bsc#1240041)
-------------------------------------------------------------------
Fri Feb 28 11:42:14 CET 2025 - rosuna@suse.com
- version 5.1.2-0
* Add mgr-ssl-cert-setup --skip-db option
* Copy generated CA rpms instead of moving them to prevent
SELinux context category issues
-------------------------------------------------------------------
Mon Oct 14 15:45:19 CEST 2024 - rosuna@suse.com
- version 5.1.1-0
* Fix parsing Authority Key Identifier when keyid is not
prefixed (bsc#1229079)
* Bump version to 5.1.0
-------------------------------------------------------------------
Thu Aug 01 09:43:48 CEST 2024 - marina.latini@suse.com
- version 5.0.7-0
* Support multiple certificates for root-ca-file and
server-cert-file
-------------------------------------------------------------------
Wed Jun 26 15:02:21 CEST 2024 - marina.latini@suse.com
- version 5.0.6-0
* Remove PKI of Salt Minion only in case of changing the Master
with the bootstrap script
-------------------------------------------------------------------
Mon Jun 10 16:47:59 CEST 2024 - marina.latini@suse.com
- version 5.0.5-0
* Move server-only functions out of uyuni.common (bsc#1222325)
* Calculate bootstrap repo path for SL Micro 6.0
-------------------------------------------------------------------
Fri May 03 14:56:39 CEST 2024 - marina.latini@suse.com
- version 5.0.4-0
* Fix SUSE Liberty Linux bootstrapping when Zypper is installed
(bsc#1222347)
* Apply reboot method changes for transactional systems in the
bootstrap script
-------------------------------------------------------------------
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
07070100000017000081B400000000000000000000000168F09C4700000021000000000000000000000000000000000000004D00000000spacewalk-certs-tools/spacewalk-certs-tools.changes.agraul.pylint-everything- Allow existing pylint failures
07070100000018000081B400000000000000000000000168F09C4700000022000000000000000000000000000000000000004A00000000spacewalk-certs-tools/spacewalk-certs-tools.changes.agraul.reformat-black- Reformat Python code with black
07070100000019000081B400000000000000000000000168F09C470000004F000000000000000000000000000000000000006A00000000spacewalk-certs-tools/spacewalk-certs-tools.changes.carlo.uyuni-1246035-bootstrap-script-profile-name-var- Correct PROFILE_NAME variable definition in bootstrap script
(bsc#1246035)
0707010000001A000081B400000000000000000000000168F09C4700000020000000000000000000000000000000000000005200000000spacewalk-certs-tools/spacewalk-certs-tools.changes.mcalmer.fix-changelog-formats- Fix syntax error in changelog
0707010000001B000081B400000000000000000000000168F09C470000134B000000000000000000000000000000000000003100000000spacewalk-certs-tools/spacewalk-certs-tools.spec#
# spec file for package spacewalk-certs-tools
#
# Copyright (c) 2025 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
# The productprettyname macros is controlled in the prjconf. If not defined, we fallback here
%{!?productprettyname: %global productprettyname Uyuni}
%if 0%{?suse_version}
%global pub_dir /srv/www/htdocs/pub
%else
%global pub_dir %{_localstatedir}/www/html/pub
%endif
%global pub_bootstrap_dir %{pub_dir}/bootstrap
%global rhnroot %{_datadir}/rhn
%global __python %{_bindir}/python3
Name: spacewalk-certs-tools
Version: 5.2.0
Release: 0
Summary: %{productprettyname} SSL Key/Cert Tool
License: GPL-2.0-only
# FIXME: use correct group or remove it, see "https://en.opensuse.org/openSUSE:Package_group_guidelines"
Group: Applications/Internet
URL: https://github.com/uyuni-project/uyuni
#!CreateArchive: %{name}
Source0: https://github.com/uyuni-project/uyuni/archive/%{name}-%{version}.tar.gz
BuildRequires: docbook-utils
BuildRequires: make
Requires: openssl
Requires: rpm-build
Requires: spacewalk-base-minimal-config
Requires: sudo
Requires: tar
Requires(post): python3-rhnlib
Requires(post): python3-rpm
Requires(post): python3-uyuni-common-libs
Requires(pre): python3-%{name} = %{version}-%{release}
BuildArch: noarch
%if 0%{?suse_version}
BuildRequires: filesystem
Requires: susemanager-build-keys-web
%endif
%description
This package contains tools to generate the SSL certificates required by
%{productprettyname}.
%package -n python3-%{name}
Summary: %{productprettyname} SSL Key/Cert Tool
# FIXME: use correct group or remove it, see "https://en.opensuse.org/openSUSE:Package_group_guidelines"
Group: Applications/Internet
BuildRequires: python3
BuildRequires: python3-rpm-macros
Requires: %{name} = %{version}-%{release}
Requires: python3-uyuni-common-libs
Requires: spacewalk-backend
%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 %{buildroot}%{rhnroot}/certs
sed -i '1s|python\b|python3|' rhn-ssl-tool mgr-package-rpm-certificate-osimage rhn-bootstrap
make -f Makefile.certs install PREFIX=%{buildroot} ROOT=%{rhnroot} \
PYTHONPATH=%{python3_sitelib} PYTHONVERSION=%{python3_version} \
MANDIR=%{_mandir} PUB_BOOTSTRAP_DIR=%{pub_bootstrap_dir}
ln -s rhn-ssl-tool-%{python3_version} %{buildroot}%{_bindir}/rhn-ssl-tool
ln -s mgr-ssl-cert-setup-%{python3_version} %{buildroot}%{_bindir}/mgr-ssl-cert-setup
ln -s rhn-bootstrap-%{python3_version} %{buildroot}%{_bindir}/rhn-bootstrap
ln -s mgr-ssl-tool.1.gz %{buildroot}%{_mandir}/man1/rhn-ssl-tool.1.gz
ln -s mgr-bootstrap.1.gz %{buildroot}%{_mandir}/man1/rhn-bootstrap.1.gz
ln -s rhn-bootstrap %{buildroot}%{_bindir}/mgr-bootstrap
ln -s rhn-ssl-tool %{buildroot}%{_bindir}/mgr-ssl-tool
ln -s rhn-sudo-ssl-tool %{buildroot}%{_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
0707010000001C000081B400000000000000000000000168F09C4700000807000000000000000000000000000000000000002B00000000spacewalk-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
0707010000001D000081B400000000000000000000000168F09C470000182E000000000000000000000000000000000000002400000000spacewalk-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
0707010000001E000081B400000000000000000000000168F09C4700006005000000000000000000000000000000000000002400000000spacewalk-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
_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 Multi-Linux 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
_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]
+ [_optServerTar]
)
# 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]
)
# 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
# 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
# ===============================================================================
0707010000001F000081B400000000000000000000000168F09C4700006F6B000000000000000000000000000000000000002700000000spacewalk-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, 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",
"--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 Multi-Linux 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:
##
CA_CERT_RPM_SUMMARY = "Organizational public SSL CA certificate " "(client-side)."
# ===============================================================================
07070100000020000081B400000000000000000000000168F09C47000010D5000000000000000000000000000000000000002400000000spacewalk-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__
# ===============================================================================
07070100000021000081B400000000000000000000000168F09C4700000970000000000000000000000000000000000000002100000000spacewalk-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()
07070100000022000081B400000000000000000000000168F09C4700000C6A000000000000000000000000000000000000002E00000000spacewalk-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!!!