Revised and CIS introduced

This commit is contained in:
m8in
2025-02-15 18:33:27 +01:00
parent 3ea1ac0915
commit a0a9501c31
12 changed files with 318 additions and 120 deletions

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
# Ignore the file '/domainOfHostOwner' because this is per host individually.
/domainOfHostOwner
# Ignore the subfolders only, because their content are other git repositories.
# But 'definitions and 'states' should be prepared by cloning this repository.
/definitions/*/
/states/*/
# Ignore environment files
.env

View File

@@ -1,5 +1,5 @@
Infrastructure System (ISS) Core Infrastructure System (CIS)
=========================== ================================
Setup a new host Setup a new host
---------------- ----------------
@@ -52,20 +52,20 @@ We use the modern ed25519 keys, so the public key of root is stored at this loca
### Register public host key ### Register public host key
This is an example for `example.net` as domain of the host owner. This is an example for `example.net` as domain of the host owner.
1. Repository `iss`, allow __readonly__ access only. 1. Repository `cis`, allow __readonly__ access only.
2. Repository `iss-definition-example.net`, allow __readonly__ access only. 2. Repository `cis-definition-example.net`, allow __readonly__ access only.
3. Repository `iss-state-example.net`, allow __writable__ access. 3. Repository `cis-state-example.net`, allow __writable__ access.
### Clone the Infrastructure System (iss) repository ### Clone the Infrastructure System (cis) repository
After you registered the printed root's public key of this host you can clone the repository and execute the setup script: After you registered the printed root's public key of this host you can clone the repository and execute the setup script:
```sh ```sh
# Note the tailing '/iss', because we want to clone the repository to that folder # Note the tailing '/cis', because we want to clone the repository to that folder
git clone ssh://git@git.example.dev:22448/iss.git /iss git clone ssh://git@git.example.dev:22448/cis.git /cis
# Execute the setup script # Execute the setup script
/iss/setupCoreOntoThisHost.sh /cis/setupCoreOntoThisHost.sh
``` ```
<br> <br>
@@ -74,7 +74,7 @@ git clone ssh://git@git.example.dev:22448/iss.git /iss
How it works How it works
------------ ------------
We add a webhook to each gitea repository that belongs to ISS: We add a webhook to each gitea repository that belongs to CIS:
- __Taget URL:__ https://YOUR.JENKINS.DOMAIN/generic-webhook-trigger/invoke?token=YOUR_TOKEN - __Taget URL:__ https://YOUR.JENKINS.DOMAIN/generic-webhook-trigger/invoke?token=YOUR_TOKEN
- __HTTP-Method:__ POST - __HTTP-Method:__ POST
- __Trigger On:__ Push Events - __Trigger On:__ Push Events
@@ -98,7 +98,7 @@ cat "${JENKINS_HOME}/.ssh/id_ed25519.pub" \
&& cat "${JENKINS_HOME}/.ssh/id_ed25519.pub") && cat "${JENKINS_HOME}/.ssh/id_ed25519.pub")
# add your host here, note the tailing '&' to run it in parallel # add your host here, note the tailing '&' to run it in parallel
ssh -o StrictHostKeyChecking=no jenkins@192.168.X.Y /iss/update_repositories.sh ( --scripts | --definitions | --states ) & ssh -o StrictHostKeyChecking=no jenkins@192.168.X.Y /cis/update_repositories.sh ( --scripts | --definitions | --states ) &
#wait for all background processes to complete #wait for all background processes to complete
wait wait

View File

@@ -73,7 +73,7 @@ function cloneOrPull {
function addAndCheckGitRepository() { function addAndCheckGitRepository() {
local _FOLDER _REPOSITORY local _FOLDER _REPOSITORY
_FOLDER="${1:?"Missing first parameter FOLDER"}" _FOLDER="${1:?"Missing first parameter FOLDER"}"
_REPOSITORY="${2:?"Missing second parameter REPOSITORY: e.g. ssh://git@your.domain.com/iss.git "}" _REPOSITORY="${2:?"Missing second parameter REPOSITORY: e.g. ssh://git@your.domain.com/cis.git "}"
_RIGHTS="${3:?"Missing third parameter RIGHTS: (readonly, writable) "}" _RIGHTS="${3:?"Missing third parameter RIGHTS: (readonly, writable) "}"
readonly _FOLDER _REPOSITORY readonly _FOLDER _REPOSITORY
@@ -98,4 +98,6 @@ addAndCheckGitRepository \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
"$(echo ${2} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ "$(echo ${2} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
"$(echo ${3} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ "$(echo ${3} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0 || exit 1 && exit 0
exit 1

View File

@@ -34,6 +34,7 @@ function addNormalUser() {
} }
# sanitizes all parameters # sanitizes all parameters
addNormalUser \ addNormalUser "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ && exit 0
&& exit 0 || exit 1
exit 1

View File

@@ -8,11 +8,11 @@
# Note that an unprivileged user can use this script successfully, # Note that an unprivileged user can use this script successfully,
# if no user has to be added to the host because it already exists. # if no user has to be added to the host because it already exists.
function addToCrontabEveryHour() { function addToCrontabEveryHour() {
local _ROOT _MINUTE_VALUE _STRING local _CIS_ROOT _MINUTE_VALUE _STRING
_ROOT="${0%%/core/*}/" #Removes longest matching pattern '/core/*' from the end _CIS_ROOT="${0%%/core/*}/" #Removes longest matching pattern '/core/*' from the end
! [ -z "${2##*[!0-9]*}" ] && _MINUTE_VALUE=$((${2}%60)) # if second parameter is integer then (minute-value % 60) as safe guard ! [ -z "${2##*[!0-9]*}" ] && _MINUTE_VALUE=$((${2}%60)) # if second parameter is integer then (minute-value % 60) as safe guard
_STRING="${_MINUTE_VALUE:?"Missing MINUTE_VALUE"} * * * * ${1:?"Missing first parameter COMMAND"} > /dev/null 2>&1" _STRING="${_MINUTE_VALUE:?"Missing MINUTE_VALUE"} * * * * ${1:?"Missing first parameter COMMAND"} > /dev/null 2>&1"
readonly _ROOT _MINUTE_VALUE _STRING readonly _CIS_ROOT _MINUTE_VALUE _STRING
[ "$(id -u)" == "0" ] \ [ "$(id -u)" == "0" ] \
&& crontab -l | grep -qF "${_STRING:?"Missing CRON_STRING"}" \ && crontab -l | grep -qF "${_STRING:?"Missing CRON_STRING"}" \
@@ -21,11 +21,11 @@ function addToCrontabEveryHour() {
&& return 0 && return 0
[ "$(id -u)" == "0" ] \ [ "$(id -u)" == "0" ] \
&& echo "${_ROOT:?"Missing ROOT"}" | grep "home" &> /dev/null \ && echo "${_CIS_ROOT:?"Missing CIS_ROOT"}" | grep "home" &> /dev/null \
&& echo "SUCCESS: Although the entry will be skipped: ("$(readlink -f ${0})")" \ && echo "SUCCESS: Although the entry will be skipped: ("$(readlink -f ${0})")" \
&& echo " - '${_STRING}'" \ && echo " - '${_STRING}'" \
&& echo " that is because the current environment is:" \ && echo " that is because the current environment is:" \
&& echo " - ${_ROOT}" \ && echo " - ${_CIS_ROOT}" \
&& return 0 && return 0
[ "$(id -u)" == "0" ] \ [ "$(id -u)" == "0" ] \
@@ -47,4 +47,6 @@ function addToCrontabEveryHour() {
addToCrontabEveryHour \ addToCrontabEveryHour \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
"$(echo ${2} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ "$(echo ${2} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0 || exit 1 && exit 0
exit 1

View File

@@ -51,17 +51,17 @@ function prepareFolder() {
} }
function defineAuthorizedKeysOfUser() { function defineAuthorizedKeysOfUser() {
local _ROOT _CORE_SCRIPTS _DOMAIN _DEFINITIONS _USER local _CIS_ROOT _CORE_SCRIPTS _DOMAIN _DEFINITIONS _USER
_DEFINITIONS="$(realpath -s "${1:?"Missing first parameter DEFINITIONS: 'ROOT/definitions/DOMAIN'"}")" _DEFINITIONS="$(realpath -s "${1:?"Missing first parameter DEFINITIONS: 'ROOT/definitions/DOMAIN'"}")"
_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end _CIS_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end
_DOMAIN="${_DEFINITIONS##*/definitions/}" #Removes longest matching pattern '*/definitions/' from the begin _DOMAIN="${_DEFINITIONS##*/definitions/}" #Removes longest matching pattern '*/definitions/' from the begin
_DOMAIN="${_DOMAIN%/}" #Removes shortest matching pattern '/' from the end _DOMAIN="${_DOMAIN%/}" #Removes shortest matching pattern '/' from the end
#Build from components for safety #Build from components for safety
_DEFINITIONS="${_ROOT:?"Missing ROOT"}definitions/${_DOMAIN:?"Missing DOMAIN"}" _DEFINITIONS="${_CIS_ROOT:?"Missing ROOT"}definitions/${_DOMAIN:?"Missing DOMAIN"}"
_USER="${2:?"Missing second parameter USER"}" _USER="${2:?"Missing second parameter USER"}"
_CORE_SCRIPTS="${_ROOT:?"Missing ROOT"}core/" _CORE_SCRIPTS="${_CIS_ROOT:?"Missing ROOT"}core/"
readonly _ROOT _CORE_SCRIPTS _DOMAIN _DEFINITIONS _USER readonly _CIS_ROOT _CORE_SCRIPTS _DOMAIN _DEFINITIONS _USER
case "${_USER:?"Missing USER"}" in case "${_USER:?"Missing USER"}" in
root) root)
@@ -83,4 +83,6 @@ function defineAuthorizedKeysOfUser() {
defineAuthorizedKeysOfUser \ defineAuthorizedKeysOfUser \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
"$(echo ${2} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ "$(echo ${2} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0 || exit 1 && exit 0
exit 1

View File

@@ -24,6 +24,28 @@ function isCoreDefinition() {
return 1 return 1
} }
function filterInvalidAuthorizedKeysFilesOfRoot() {
local _FILE_DEFINED
_FILE_DEFINED="${1:?"Missing DEFINITION FILE"}"
readonly _FILE_DEFINED
#If the full filename contains 'root/.ssh/authorized_keys' then check the content.
#Skip lines starting with '#' and if at least one remaining line contains 'ssh' and '@' then print the filename.
echo "${_FILE_DEFINED}" | grep -F 'root/.ssh/authorized_keys' &> /dev/null \
&& grep -vE '^[[:blank:]]*#' "${_FILE_DEFINED}" | grep -F 'ssh' | grep -F '@' &> /dev/null \
&& echo "${_FILE_DEFINED}" \
&& return 0
#If the full filename contains 'root/.ssh/authorized_keys' print nothing because the file has to be invalid.
echo "${_FILE_DEFINED}" | grep -F 'root/.ssh/authorized_keys' &> /dev/null \
&& echo \
&& return 0
#Print the full filename because it does not contain 'root/.ssh/authorized_keys'
echo "${_FILE_DEFINED}"
return 0
}
function printSelectedDefinition() { function printSelectedDefinition() {
local _CORE_FILE_DEFINED_ALL_HOSTS _CORE_FILE_DEFINED_THIS_HOST _FILE_DEFINED_ALL_HOSTS _FILE_DEFINED_THIS_HOST local _CORE_FILE_DEFINED_ALL_HOSTS _CORE_FILE_DEFINED_THIS_HOST _FILE_DEFINED_ALL_HOSTS _FILE_DEFINED_THIS_HOST
_CORE_FILE_DEFINED_ALL_HOSTS="${1:?"Missing DEFINITIONS"}/core/all${2:?"Missing CURRENT_FULLFILE"}" _CORE_FILE_DEFINED_ALL_HOSTS="${1:?"Missing DEFINITIONS"}/core/all${2:?"Missing CURRENT_FULLFILE"}"
@@ -36,13 +58,13 @@ function printSelectedDefinition() {
#Try this host first because it should be priorized. #Try this host first because it should be priorized.
isCoreDefinition "${2:?"Missing CURRENT_FULLFILE"}" \ isCoreDefinition "${2:?"Missing CURRENT_FULLFILE"}" \
&& [ -s "${_CORE_FILE_DEFINED_THIS_HOST}" ] \ && [ -s "${_CORE_FILE_DEFINED_THIS_HOST}" ] \
&& echo "${_CORE_FILE_DEFINED_THIS_HOST}" \ && filterInvalidAuthorizedKeysFiles "${_CORE_FILE_DEFINED_THIS_HOST}" \
&& return 0 && return 0
#The following are special definitions that affect the core functionality. #The following are special definitions that affect the core functionality.
isCoreDefinition "${2:?"Missing CURRENT_FULLFILE"}" \ isCoreDefinition "${2:?"Missing CURRENT_FULLFILE"}" \
&& [ -s "${_CORE_FILE_DEFINED_ALL_HOSTS}" ] \ && [ -s "${_CORE_FILE_DEFINED_ALL_HOSTS}" ] \
&& echo "${_CORE_FILE_DEFINED_ALL_HOSTS}" \ && filterInvalidAuthorizedKeysFiles "${_CORE_FILE_DEFINED_ALL_HOSTS}" \
&& return 0 && return 0
#Try this host first because it should be priorized. #Try this host first because it should be priorized.
@@ -71,11 +93,6 @@ function createSymlinkToDefinition() {
&& [ "$(sha256sum "${_DEFINED_FULLFILE}" | cut -d' ' -f1)" == "$(sha256sum "${_CURRENT_FULLFILE}" | cut -d' ' -f1)" ] \ && [ "$(sha256sum "${_DEFINED_FULLFILE}" | cut -d' ' -f1)" == "$(sha256sum "${_CURRENT_FULLFILE}" | cut -d' ' -f1)" ] \
&& echo "The content of the current file already matches the definition, but it will be replaced by a symlink..." && echo "The content of the current file already matches the definition, but it will be replaced by a symlink..."
[ -f "${_CURRENT_FULLFILE}" ] \
&& [ "$(sha256sum "${_DEFINED_FULLFILE}" | cut -d' ' -f1)" == "$(sha256sum "${_CURRENT_FULLFILE}" | cut -d' ' -f1)" ] \
&& echo "The content of the current file already matches the definition, but it will be replaced by a symlink..."
[ -f "${_CURRENT_FULLFILE}" ] \ [ -f "${_CURRENT_FULLFILE}" ] \
&& mv "${_CURRENT_FULLFILE:?"Missing CURRENT_FULLFILE"}" "${_SAVED_FULLFILE:?"Missing SAVED_FULLFILE"}" \ && mv "${_CURRENT_FULLFILE:?"Missing CURRENT_FULLFILE"}" "${_SAVED_FULLFILE:?"Missing SAVED_FULLFILE"}" \
&& echo "Current file has been backed up to: '${_SAVED_FULLFILE}'" && echo "Current file has been backed up to: '${_SAVED_FULLFILE}'"
@@ -92,13 +109,13 @@ function createSymlinkToDefinition() {
} }
function ensureUsageOfDefinitions() { function ensureUsageOfDefinitions() {
local _ROOT _CURRENT_FILE _CURRENT_FOLDER _CURRENT_FULLFILE _DEFINITIONS _DOMAIN _DEFINED_FULLFILE _NOW _SAVED_FULLFILE local _CIS_ROOT _CURRENT_FILE _CURRENT_FOLDER _CURRENT_FULLFILE _DEFINITIONS _DOMAIN _DEFINED_FULLFILE _NOW _SAVED_FULLFILE
_DEFINITIONS="$(realpath -s "${1:?"Missing first parameter DEFINITIONS: 'ROOT/definitions/DOMAIN'"}")" _DEFINITIONS="$(realpath -s "${1:?"Missing first parameter DEFINITIONS: 'ROOT/definitions/DOMAIN'"}")"
_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end _CIS_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end
_DOMAIN="${_DEFINITIONS##*/definitions/}" #Removes longest matching pattern '*/definitions/' from the begin _DOMAIN="${_DEFINITIONS##*/definitions/}" #Removes longest matching pattern '*/definitions/' from the begin
_DOMAIN="${_DOMAIN%/}" #Removes shortest matching pattern '/' from the end _DOMAIN="${_DOMAIN%/}" #Removes shortest matching pattern '/' from the end
#Build from components for safety #Build from components for safety
_DEFINITIONS="$(printIfEqual "${_DEFINITIONS}" "${_ROOT:?"Missing ROOT"}definitions/${_DOMAIN:?"Missing DOMAIN"}")" _DEFINITIONS="$(printIfEqual "${_DEFINITIONS}" "${_CIS_ROOT:?"Missing ROOT"}definitions/${_DOMAIN:?"Missing DOMAIN"}")"
_CURRENT_FOLDER="$(dirname "${2:?"Missing second parameter CURRENT_FULLFILE"}")" _CURRENT_FOLDER="$(dirname "${2:?"Missing second parameter CURRENT_FULLFILE"}")"
@@ -121,7 +138,13 @@ function ensureUsageOfDefinitions() {
_DEFINED_FULLFILE="$(printSelectedDefinition "${_DEFINITIONS}" "${_CURRENT_FULLFILE}")" _DEFINED_FULLFILE="$(printSelectedDefinition "${_DEFINITIONS}" "${_CURRENT_FULLFILE}")"
_NOW="$(date +%Y%m%d_%H%M)" _NOW="$(date +%Y%m%d_%H%M)"
_SAVED_FULLFILE="${_CURRENT_FULLFILE}-backup@${_NOW:?"Missing NOW"}" _SAVED_FULLFILE="${_CURRENT_FULLFILE}-backup@${_NOW:?"Missing NOW"}"
readonly _ROOT _CURRENT_FILE _CURRENT_FOLDER _CURRENT_FULLFILE _DEFINITIONS _DOMAIN _DEFINED_FULLFILE _NOW _SAVED_FULLFILE readonly _CIS_ROOT _CURRENT_FILE _CURRENT_FOLDER _CURRENT_FULLFILE _DEFINITIONS _DOMAIN _DEFINED_FULLFILE _NOW _SAVED_FULLFILE
[ -z "${_DEFINED_FULLFILE}" ] \
&& echo \
&& echo "URGENT WARNING: If an 'authorized_keys' file of root is replaced by an invalid version," \
&& echo " you may lose access to this host!" \
&& echo
! [ -f "${_DEFINED_FULLFILE}" ] \ ! [ -f "${_DEFINED_FULLFILE}" ] \
&& echo "FAIL: No definition available for this file: ("$(readlink -f ${0})")" \ && echo "FAIL: No definition available for this file: ("$(readlink -f ${0})")" \
@@ -138,11 +161,11 @@ function ensureUsageOfDefinitions() {
&& echo " - '${_DEFINED_FULLFILE}'" \ && echo " - '${_DEFINED_FULLFILE}'" \
&& return 0 && return 0
echo "${_ROOT:?"Missing ROOT"}" | grep "home" &> /dev/null \ echo "${_CIS_ROOT:?"Missing CIS_ROOT"}" | grep "home" &> /dev/null \
&& echo "SUCCESS: Although this definition will be skipped: ("$(readlink -f ${0})")" \ && echo "SUCCESS: Although this definition will be skipped: ("$(readlink -f ${0})")" \
&& echo " - '${_DEFINED_FULLFILE}'" \ && echo " - '${_DEFINED_FULLFILE}'" \
&& echo " that is because the current environment is:" \ && echo " that is because the current environment is:" \
&& echo " - ${_ROOT}" \ && echo " - ${_CIS_ROOT}" \
&& echo " following file is in use:" \ && echo " following file is in use:" \
&& echo " - $(readlink -f "${_CURRENT_FULLFILE}")" \ && echo " - $(readlink -f "${_CURRENT_FULLFILE}")" \
&& return 0 && return 0
@@ -174,4 +197,6 @@ function ensureUsageOfDefinitions() {
ensureUsageOfDefinitions \ ensureUsageOfDefinitions \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
"$(echo ${2} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ "$(echo ${2} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0 || exit 1 && exit 0
exit 1

View File

@@ -0,0 +1,83 @@
#!/bin/bash
[ "$(id -u)" == "0" ] \
&& echo "This script prepares the content of the repository for the definitions." \
&& echo "You have run it as root, please run it with a user who has write access to the Git server." \
&& echo \
&& echo "Do not use the SSH key of root for this." \
&& echo \
&& exit 1
_BOOT_HOSTNAME="$(hostname -b)"
_BOOT_DOMAIN="${_BOOT_HOSTNAME#*.}" #Removes shortest matching pattern '*.' from the begin to get the domain
[ -z "${_BOOT_DOMAIN}" ] \
&& echo "It was impossible to find out the domain of this host, please prepare this host first." \
&& exit 1
_REOPSITORY_NAME="cis-definition-${_BOOT_DOMAIN}"
#Generate file 'README.md'
mkdir -p /tmp/skeleton/definition
cat << EOF > /tmp/skeleton/definition/README.md
#$_REOPSITORY_NAME
Central Infrastructure System's definition of domain $_BOOT_DOMAIN
EOF
#Generate sudoers file 'allow-jenkins-updateRepositories'
mkdir -p /tmp/skeleton/definition/core/all/etc/sudoers.d
cat << EOF > /tmp/skeleton/definition/core/all/etc/sudoers.d/allow-jenkins-updateRepositories
Cmnd_Alias C_JENKINS = \\
/cis/updateRepositories.sh --core, \\
/cis/updateRepositories.sh --scripts, \\
/cis/updateRepositories.sh --definitions, \\
/cis/updateRepositories.sh --states
jenkins ALL = (root) NOPASSWD: C_JENKINS
EOF
#Generate file 'authorized_keys' for user jenkins
mkdir -p /tmp/skeleton/definition/core/all/home/jenkins/.ssh
cat << EOF > /tmp/skeleton/definition/core/all/home/jenkins/.ssh/authorized_keys
#------------------------------------------------------
# Enter the public ssh key of your jenkins server here.
#------------------------------------------------------
EOF
#Use current file 'authorized_keys' of root as definition
mkdir -p /tmp/skeleton/definition/core/all/root/.ssh
cp /root/.ssh/authorized_keys /tmp/skeleton/definition/core/all/root/.ssh/authorized_keys
cat << EOF
The first content for your repository for the definitions of the '$_BOOT_DOMAIN' domain has been created.
Please create a definition repository.
To follow the naming convention name it '$_REOPSITORY_NAME'
Go to folder '/tmp/skeleton/definition' and check the content of all 'authorized_keys' files,
correct them if required to prevent losing access to your hosts.
The public ssh key of your jenkins server has to be added.
Only now follow the instructions as our git server shows.
For example:
git init
git checkout -b main
git add .
git commit -m "first core definitions"
git remote add origin ssh://git@git.example.dev:22448/$_REOPSITORY_NAME.git
git push -u origin main
EOF

View File

@@ -0,0 +1,49 @@
#!/bin/bash
[ "$(id -u)" == "0" ] \
&& echo "This script prepares the content of the repository for the definitions." \
&& echo "You have run it as root, please run it with a user who has write access to the Git server." \
&& echo \
&& echo "Do not use the SSH key of root for this." \
&& echo \
&& exit 1
_BOOT_HOSTNAME="$(hostname -b)"
_BOOT_DOMAIN="${_BOOT_HOSTNAME#*.}" #Removes shortest matching pattern '*.' from the begin to get the domain
[ -z "${_BOOT_DOMAIN}" ] \
&& echo "It was impossible to find out the domain of this host, please prepare this host first." \
&& exit 1
_REOPSITORY_NAME="cis-state-${_BOOT_DOMAIN}"
#Generate README.md
mkdir -p /tmp/skeleton/state
cat << EOF > /tmp/skeleton/state/README.md
#$_REOPSITORY_NAME
Central Infrastructure System's state of domain $_BOOT_DOMAIN
EOF
cat << EOF
The first content for your repository for the state of the '$_BOOT_DOMAIN' domain has been created.
Please create a states repository.
To follow the naming convention name it '$_REOPSITORY_NAME'
Then go to folder '/tmp/skeleton/state' and follow the instructions as your git server shows.
For example:
git init
git checkout -b main
git add .
git commit -m "first state"
git remote add origin ssh://git@git.example.dev:22448/$_REOPSITORY_NAME.git
git push -u origin main
EOF

View File

@@ -12,42 +12,62 @@ function setNeededHostnameOrExit() {
&& hostnamectl set-hostname "${_FQDN}" \ && hostnamectl set-hostname "${_FQDN}" \
&& return 0 && return 0
echo "FAILED: setting full qualified domain name, given value was:" echo "FAILED: setting full qualified domain name does not contain a domain,"
echo " - ${_FQDN}" echo " given value was: ${_FQDN}"
exit 1 exit 1
} }
function prepare() { function prepareThisHost() {
git --version > /dev/null || (apt update; apt upgrade -y; apt install git) git --version > /dev/null || (apt update; apt upgrade -y; apt install git)
echo echo
echo "Public SSH-Key for root@$(hostname -b):" echo "Public SSH-Key for root@$(hostname -b):"
cat "/root/.ssh/id_ed25519.pub" \
&& return 0
# -t type of the key pair # -t type of the key pair
# -f defines the filenames (we use the standard for the selected type here) # -f defines the filenames (we use the standard for the selected type here)
# -q quiet, no output or interaction # -q quiet, no output or interaction
# -N "" means the private key will not be secured by a passphrase # -N "" means the private key will not be secured by a passphrase
# -C defines a comment # -C defines a comment
cat "/root/.ssh/id_ed25519.pub" \ ssh-keygen \
|| (ssh-keygen \
-t ed25519 \ -t ed25519 \
-f "/root/.ssh/id_ed25519" -q -N "" \ -f "/root/.ssh/id_ed25519" -q -N "" \
-C "$(date +%Y%m%d)-root@$(hostname -b)" \ -C "$(date +%Y%m%d):root@$(hostname -b)"
&& cat "/root/.ssh/id_ed25519.pub")
cat "/root/.ssh/id_ed25519.pub" \
&& return 0
echo echo
echo "Now you have to register the public ssh-key from above into your git-server to grant these access rights:" echo "FAILED: somthing went wrong during the generation the ssh keys."
echo " These keys are mandantory. You can try to restart this script."
echo
}
function showFurtherSteps() {}
echo
echo "IMPORTANT: It is assumed that repositories for definitions and states already exist"
echo " and comply with the naming convention."
echo " Otherwise, these repositories must be created first!"
echo
echo "To grant the correct access rights, you have to register the above-mentioned ssh key,"
echo "as deploy key in these repositories of the Git server:"
echo " - scripts repository (allow readonly access only)," echo " - scripts repository (allow readonly access only),"
echo " - definitions repository (allow readonly access only)," echo " - definitions repository (allow readonly access only),"
echo " - states repository (allow writable access)." echo " - states repository (allow writable access)."
echo echo
echo "After all access rights are granted you can clone the Infrastructure System:" echo "After all access rights are granted you can clone the Core Infrastructure System:"
echo " e.g.: git clone ssh://git@git.example.dev:22448/iss.git /iss" echo " e.g.: git clone ssh://git@git.example.dev:22448/cis.git /cis"
echo echo
echo "Finally call 'setupCoreOntoThisHost.sh' from the root directory of the repository:" echo "Finally call 'setupCoreOntoThisHost.sh' from the root directory:"
echo " e.g.: /iss/setupCoreOntoThisHost.sh" echo " e.g.: /cis/setupCoreOntoThisHost.sh"
echo echo
} }
# sanitizes all parameters # sanitizes all parameters
setNeededHostnameOrExit "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ setNeededHostnameOrExit "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& prepare && prepareThisHost \
&& showFurtherSteps \
&& exit 0
exit 1

View File

@@ -31,10 +31,10 @@ function checkGitIsAvailable() {
} }
function checkPreconditions() { function checkPreconditions() {
local _ROOT _DOMAIN local _CIS_ROOT _DOMAIN
_ROOT="${1:?"Missing parameter ROOT"}" _CIS_ROOT="${1:?"Missing parameter CIS_ROOT"}"
_DOMAIN="${2}" # Optional parameter DOMAIN _DOMAIN="${2}" # Optional parameter DOMAIN
readonly _ROOT _DOMAIN readonly _CIS_ROOT _DOMAIN
! [ -z "${_DOMAIN}" ] \ ! [ -z "${_DOMAIN}" ] \
&& [ "$(hostname -d)" != "${_DOMAIN}" ] \ && [ "$(hostname -d)" != "${_DOMAIN}" ] \
@@ -46,7 +46,7 @@ function checkPreconditions() {
! [ -z "${_DOMAIN}" ] \ ! [ -z "${_DOMAIN}" ] \
&& checkPathsAreAvaiable \ && checkPathsAreAvaiable \
&& checkGitIsAvailable \ && checkGitIsAvailable \
&& git -C "${_ROOT}" pull &> /dev/null \ && git -C "${_CIS_ROOT}" pull &> /dev/null \
&& return 0 && return 0
echo echo
@@ -69,11 +69,11 @@ function checkPreconditions() {
} }
function getOrSetDomain() { function getOrSetDomain() {
local _ROOT _DOMAIN_FILE _GIVEN_DOMAIN local _CIS_ROOT _DOMAIN_FILE _GIVEN_DOMAIN
_ROOT="${1:?"Missing parameter ROOT"}" _CIS_ROOT="${1:?"Missing parameter CIS_ROOT"}"
_DOMAIN_FILE="${_ROOT:?"Missing ROOT"}domainOfHostOwner" _DOMAIN_FILE="${_CIS_ROOT:?"Missing CIS_ROOT"}domainOfHostOwner"
_GIVEN_DOMAIN="${2}" # Optional parameter DOMAIN _GIVEN_DOMAIN="${2}" # Optional parameter DOMAIN
readonly _ROOT _DOMAIN_FILE _GIVEN_DOMAIN readonly _CIS_ROOT _DOMAIN_FILE _GIVEN_DOMAIN
# Wenn DOMAIN_FILE enhält lesbare Daten # Wenn DOMAIN_FILE enhält lesbare Daten
grep '[^[:space:]]' "${_DOMAIN_FILE:?"Missing DOMAIN_FILE"}" &> /dev/null \ grep '[^[:space:]]' "${_DOMAIN_FILE:?"Missing DOMAIN_FILE"}" &> /dev/null \
@@ -99,11 +99,11 @@ function getOrSetDomain() {
} }
function getRemoteRepositoryPath() { function getRemoteRepositoryPath() {
local _ROOT local _CIS_ROOT
_ROOT="${1:?"Missing parameter ROOT"}" _CIS_ROOT="${1:?"Missing parameter CIS_ROOT"}"
readonly _ROOT readonly _CIS_ROOT
_RESULT="$(git -C "${_ROOT:?"Missing ROOT"}" remote show origin | grep -i 'fetch' | xargs -n 1 | grep -i 'ssh://')" _RESULT="$(git -C "${_CIS_ROOT:?"Missing CIS_ROOT"}" remote show origin | grep -i 'fetch' | xargs -n 1 | grep -i 'ssh://')"
_RESULT="${_RESULT%/*}" #Removes shortest matching pattern '/*' from the end _RESULT="${_RESULT%/*}" #Removes shortest matching pattern '/*' from the end
! [ -z "${_RESULT}" ] \ ! [ -z "${_RESULT}" ] \
&& echo "${_RESULT}" \ && echo "${_RESULT}" \
@@ -113,12 +113,12 @@ function getRemoteRepositoryPath() {
} }
function addDefinition(){ function addDefinition(){
local _ROOT _CORE_SCRIPTS _DEFINITIONS _REPOSITORY local _CIS_ROOT _CORE_SCRIPTS _DEFINITIONS _REPOSITORY
_DEFINITIONS="${1:?"Missing parameter DEFINITIONS"}" _DEFINITIONS="${1:?"Missing parameter DEFINITIONS"}"
_REPOSITORY="${2:?"Missing parameter REPOSITORY"}" _REPOSITORY="${2:?"Missing parameter REPOSITORY"}"
_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end _CIS_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end
_CORE_SCRIPTS="${_ROOT:?"Missing ROOT"}core/" _CORE_SCRIPTS="${_CIS_ROOT:?"Missing CIS_ROOT"}core/"
readonly _ROOT _CORE_SCRIPTS _DEFINITIONS _REPOSITORY readonly _CIS_ROOT _CORE_SCRIPTS _DEFINITIONS _REPOSITORY
[ "$(id -u)" == "0" ] \ [ "$(id -u)" == "0" ] \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_DEFINITIONS}" "${_REPOSITORY}" readonly \ && "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_DEFINITIONS}" "${_REPOSITORY}" readonly \
&& echo " - definitions are usable for this host." \ && echo " - definitions are usable for this host." \
@@ -133,12 +133,12 @@ function addDefinition(){
} }
function addState() { function addState() {
local _ROOT _CORE_SCRIPTS _STATES _REPOSITORY local _CIS_ROOT _CORE_SCRIPTS _STATES _REPOSITORY
_STATES="${1:?"Missing parameter STATES"}" _STATES="${1:?"Missing parameter STATES"}"
_REPOSITORY="${2:?"Missing parameter REPOSITORY"}" _REPOSITORY="${2:?"Missing parameter REPOSITORY"}"
_ROOT="${_STATES%%/states/*}/" #Removes longest matching pattern '/states/*' from the end _CIS_ROOT="${_STATES%%/states/*}/" #Removes longest matching pattern '/states/*' from the end
_CORE_SCRIPTS="${_ROOT:?"Missing ROOT"}core/" _CORE_SCRIPTS="${_CIS_ROOT:?"Missing CIS_ROOT"}core/"
readonly _ROOT _CORE_SCRIPTS _STATES _REPOSITORY readonly _CIS_ROOT _CORE_SCRIPTS _STATES _REPOSITORY
[ "$(id -u)" == "0" ] \ [ "$(id -u)" == "0" ] \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_STATES}" "${_REPOSITORY}" writable \ && "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_STATES}" "${_REPOSITORY}" writable \
@@ -154,13 +154,13 @@ function addState() {
} }
function setupCoreFunctionality() { function setupCoreFunctionality() {
local _ROOT _CORE_SCRIPTS _DEFINITIONS _MINUTE_FROM_OWN_IP _SETUP local _CIS_ROOT _CORE_SCRIPTS _DEFINITIONS _MINUTE_FROM_OWN_IP _SETUP
_DEFINITIONS="${1:?"Missing DEFINITIONS: 'ROOT/definitions/DOMAIN'"}" _DEFINITIONS="${1:?"Missing DEFINITIONS: 'ROOT/definitions/DOMAIN'"}"
_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end _CIS_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end
_CORE_SCRIPTS="${_ROOT:?"Missing ROOT"}core/" _CORE_SCRIPTS="${_CIS_ROOT:?"Missing CIS_ROOT"}core/"
_MINUTE_FROM_OWN_IP="$(hostname -I | xargs -n 1 | grep -F . | head -n 1 | cut -d. -f4 || echo 0)" #uses last value from first own ipv4 or 0 as minute value _MINUTE_FROM_OWN_IP="$(hostname -I | xargs -n 1 | grep -F . | head -n 1 | cut -d. -f4 || echo 0)" #uses last value from first own ipv4 or 0 as minute value
_SETUP="${2:?"Missing SETUP"}" _SETUP="${2:?"Missing SETUP"}"
readonly _ROOT _CORE_SCRIPTS _DEFINITIONS _MINUTE_FROM_OWN_IP _SETUP readonly _CIS_ROOT _CORE_SCRIPTS _DEFINITIONS _MINUTE_FROM_OWN_IP _SETUP
[ "$(id -u)" != "0" ] \ [ "$(id -u)" != "0" ] \
&& echo "Configuration of host skipped because of insufficient rights." \ && echo "Configuration of host skipped because of insufficient rights." \
@@ -183,20 +183,20 @@ function setupCoreFunctionality() {
} }
function setup() { function setup() {
local _ROOT _DEFINITIONS _DEFINITIONS_REPOSITORY _DOMAIN _REPOSITORY_PATH _SETUP _STATES _STATES_REPOSITORY local _CIS_ROOT _DEFINITIONS _DEFINITIONS_REPOSITORY _DOMAIN _REPOSITORY_PATH _SETUP _STATES _STATES_REPOSITORY
_SETUP="$(readlink -f "${0}" 2> /dev/null)" _SETUP="$(readlink -f "${0}" 2> /dev/null)"
_ROOT="$(dirname ${_SETUP:?"Missing SETUP"} 2> /dev/null || echo "/iss")/" _CIS_ROOT="$(dirname ${_SETUP:?"Missing SETUP"} 2> /dev/null || echo "/cis")/"
_DOMAIN="$(getOrSetDomain "${_ROOT:?"Missing ROOT"}" "${1}")" _DOMAIN="$(getOrSetDomain "${_CIS_ROOT:?"Missing CIS_ROOT"}" "${1}")"
_REPOSITORY_PATH="$(getRemoteRepositoryPath "${_ROOT:?"Missing ROOT"}")" _REPOSITORY_PATH="$(getRemoteRepositoryPath "${_CIS_ROOT:?"Missing CIS_ROOT"}")"
! checkPreconditions "${_ROOT:?"Missing ROOT"}" "${_DOMAIN}" \ ! checkPreconditions "${_CIS_ROOT:?"Missing CIS_ROOT"}" "${_DOMAIN}" \
&& return 1 && return 1
_DEFINITIONS="${_ROOT:?"Missing ROOT"}definitions/${_DOMAIN:?"Missing DOMAIN"}" _DEFINITIONS="${_CIS_ROOT:?"Missing CIS_ROOT"}definitions/${_DOMAIN:?"Missing DOMAIN"}"
_DEFINITIONS_REPOSITORY="${_REPOSITORY_PATH:?"Missing REPOSITORY_PATH"}/iss-definition-${_DOMAIN:?"Missing DOMAIN"}.git" _DEFINITIONS_REPOSITORY="${_REPOSITORY_PATH:?"Missing REPOSITORY_PATH"}/cis-definition-${_DOMAIN:?"Missing DOMAIN"}.git"
_STATES="${_ROOT:?"Missing ROOT"}states/${_DOMAIN:?"Missing DOMAIN"}" _STATES="${_CIS_ROOT:?"Missing CIS_ROOT"}states/${_DOMAIN:?"Missing DOMAIN"}"
_STATES_REPOSITORY="${_REPOSITORY_PATH:?"Missing REPOSITORY_PATH"}/iss-state-${_DOMAIN:?"Missing DOMAIN"}.git" _STATES_REPOSITORY="${_REPOSITORY_PATH:?"Missing REPOSITORY_PATH"}/cis-state-${_DOMAIN:?"Missing DOMAIN"}.git"
readonly _ROOT _DEFINITIONS _DEFINITIONS_REPOSITORY _DOMAIN _REPOSITORY_PATH _SETUP _STATES _STATES_REPOSITORY readonly _CIS_ROOT _DEFINITIONS _DEFINITIONS_REPOSITORY _DOMAIN _REPOSITORY_PATH _SETUP _STATES _STATES_REPOSITORY
echo \ echo \
&& echo "Running setup using repositories of: '${_REPOSITORY_PATH:?"Missing REPOSITORY_PATH"}' ..." \ && echo "Running setup using repositories of: '${_REPOSITORY_PATH:?"Missing REPOSITORY_PATH"}' ..." \
@@ -215,6 +215,7 @@ function setup() {
} }
# sanitizes all parameters # sanitizes all parameters
setup \ setup "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ && exit 0
&& exit 0 || exit 1
exit 1

View File

@@ -20,32 +20,32 @@
function update_repositories() { function update_repositories() {
local _ROOT _DEFINITIONS _DOMAIN _MODE _STATES _UPDATE_REPOSITORIES local _CIS_ROOT _DEFINITIONS _DOMAIN _MODE _STATES _UPDATE_REPOSITORIES
_UPDATE_REPOSITORIES="$(readlink -f "${0}" 2> /dev/null)" _UPDATE_REPOSITORIES="$(readlink -f "${0}" 2> /dev/null)"
_MODE="${1:-"all"}" _MODE="${1:-"--core"}"
_ROOT="$(dirname ${_UPDATE_REPOSITORIES:?"Missing UPDATE_REPOSITORIES"} 2> /dev/null || echo "/iss")/" _CIS_ROOT="$(dirname ${_UPDATE_REPOSITORIES:?"Missing UPDATE_REPOSITORIES"} 2> /dev/null || echo "/cis")/"
_DOMAIN="$(cat ${_ROOT:?"Missing ROOT"}domainOfHostOwner)" _DOMAIN="$(cat ${_CIS_ROOT:?"Missing CIS_ROOT"}domainOfHostOwner)"
_DEFINITIONS="${_ROOT}definitions/${_DOMAIN:?"Missing DOMAIN from file: ${_ROOT}domainOfHostOwner"}/" _DEFINITIONS="${_CIS_ROOT}definitions/${_DOMAIN:?"Missing DOMAIN from file: ${_CIS_ROOT}domainOfHostOwner"}/"
_STATES="${_ROOT}states/${_DOMAIN:?"Missing DOMAIN from file: ${_ROOT}domainOfHostOwner"}/" _STATES="${_CIS_ROOT}states/${_DOMAIN:?"Missing DOMAIN from file: ${_CIS_ROOT}domainOfHostOwner"}/"
readonly _ROOT _DEFINITIONS _DOMAIN _MODE _STATES _UPDATE_REPOSITORIES readonly _CIS_ROOT _DEFINITIONS _DOMAIN _MODE _STATES _UPDATE_REPOSITORIES
[ "${_MODE}" == "--repair" ] \ [ "${_MODE}" == "--repair" ] \
&& (git -C "${_ROOT}" reset --hard origin/master; \ && (git -C "${_CIS_ROOT}" reset --hard origin/main; \
git -C "${_DEFINITIONS}" reset --hard origin/master; \ git -C "${_DEFINITIONS}" reset --hard origin/main; \
git -C "${_STATES}" reset --hard origin/master; \ git -C "${_STATES}" reset --hard origin/main; \
echo "Run repairs") \ echo "Run repairs") \
&& return 0 && return 0
[ "${_MODE}" == "--test" ] \ [ "${_MODE}" == "--test" ] \
&& git -C "${_ROOT}" pull \ && git -C "${_CIS_ROOT}" pull \
&& git -C "${_DEFINITIONS}" pull \ && git -C "${_DEFINITIONS}" pull \
&& git -C "${_STATES}" pull \ && git -C "${_STATES}" pull \
&& echo "Run in testMode successfully." \ && echo "Run in testMode successfully." \
&& return 0 && return 0
[ "${_MODE}" == "--scripts" ] \ [ "${_MODE}" == "--scripts" ] \
&& echo "Host $HOSTNAME updating scripts: ${_ROOT} ..." \ && echo "Host $HOSTNAME updating scripts: ${_CIS_ROOT} ..." \
&& (git -C "${_ROOT}" pull &> /dev/null &) \ && (git -C "${_CIS_ROOT}" pull &> /dev/null &) \
&& return 0 && return 0
[ "${_MODE}" == "--definitions" ] \ [ "${_MODE}" == "--definitions" ] \
@@ -58,16 +58,19 @@ function update_repositories() {
&& (git -C "${_STATES}" pull &> /dev/null &) \ && (git -C "${_STATES}" pull &> /dev/null &) \
&& return 0 && return 0
echo "Host ${HOSTNAME} updating ${_MODE}:" \ [ "${_MODE}" == "--core" ] \
&& echo " - ${_ROOT}" \ && echo "Host ${HOSTNAME} updating core including scripts, definitions and states: ${_STATES} ..." \
&& echo " - ${_DEFINITIONS}" \ && (git -C "${_CIS_ROOT}" pull &> /dev/null &) \
&& echo " - ${_STATES}" && (git -C "${_DEFINITIONS}" pull &> /dev/null &) \
git -C "${_ROOT}" pull &> /dev/null && (git -C "${_STATES}" pull &> /dev/null &) \
git -C "${_DEFINITIONS}" pull &> /dev/null && return 0
git -C "${_STATES}" pull &> /dev/null
echo "FAILED: an error occurred during an update."
return 1
} }
# sanitizes all parameters # sanitizes all parameters
update_repositories \ update_repositories "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \ && exit 0
&& exit 0 || exit 1
exit 1