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-&lt;edited_name&gt;.sh | ssh root@&lt;client-hostname&gt; /bin/bash</command>
</para>
<para>
    ...or...
</para>
<para>
    <command>wget -qO- https://&lt;hostname&gt;/pub/bootstrap/bootstrap-&lt;edited_name&gt;.sh | /bin/bash</command>
</para>
<para>
    ...or...
</para>
<para>
    <command>curl -Sks https://&lt;hostname&gt;/pub/bootstrap/bootstrap-&lt;edited_name&gt;.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 &lt;rhn-admin@example.com&gt;".</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!!!
openSUSE Build Service is sponsored by