Further development into CIS (core infrastructure system)

This commit is contained in:
m8in
2025-11-27 20:42:38 +01:00
parent 3ea1ac0915
commit 0998b59fa7
15 changed files with 570 additions and 267 deletions

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
# Ignore the file '/overrideOwnDomain' because this is per host individually.
/overrideOwnDomain
# 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

137
README.md
View File

@@ -1,35 +1,109 @@
Infrastructure System (ISS)
===========================
Core Infrastructure System (CIS)
================================
Setup a new host
----------------
The main idea is to use git to keep scripts, definitions and state in sync across all hosts.
Currently an operating instance uses one repository for this core functionality and scripts,
another to distibute the definitions and a third one to share the state.
### Preconditions
To deploy the system you have to clone this repository to the host as root user.
Therefore you have to register the SSH public key of that root user as deploy key to allow readonly access to this repository.
We use the modern ed25519 keys, so the public key of root is stored at this location:
If a script or a definition has to be changed an independent working copy is needed to push the adaptions.
States can be changed by a host itself. Then we need a mechanism that informs all hosts to execute a `git pull`.
We use a Git server as syncronisation point and use a web hook to send the notification.
Because the should not be an agent to be installed on each host, we use jenkins to execute an update script via ssh.
This allows us to use standard software without having to program something that may contain a security problem.
Setup the first or a new host
-----------------------------
1. Update the host and ensure git is installed
2. Set the long hostname (fqdn)
3. Create ssh keys for user root (ssh key type ed25519)
You can use this script to do so: [prepareThisHostBeforeCloning.sh](./prepareThisHostBeforeCloning.sh)
### Ensure the existence of the repositories for your definitions and the state
This should be necessary just if you set up the first host.
You can use the following scripts to assist the process:
- [prepareDefinitionsRepository.sh](./prepareDefinitionsRepository.sh)
- [prepareStatesRepository.sh](./prepareStatesRepository.sh)
### Register the public ssh key of user root
This is an example for `example.net` as domain of the host.
1. __Scripts:__
The public ssh key of the root user must be registered as a deploy key for the this repository,
which grants __readonly access__.
A root user of a host should only be able to update the local cloned repository (`cis`) to a new version via `git pull`.
2. __Definitions:__
The public ssh key of the root user must be registered as a deploy key for the definitions repository,
which grants __readonly access__.
User root should only be able to update the local cloned repository (`cis-definition-example.net`) to a new version via `git pull`.
3. __States:__
The public ssh key of the root user must be registered as a deploy key for the states repository,
which grants __write access__.
User root should be able to push new state to the cloned repository (`cis-state-example.net`) via `git push`.
### Clone the Infrastructure System (cis) repository and complete the setup
After you registered the printed root's public key of this host you can clone the repository and execute the setup script:
```sh
# Note the tailing '/cis', because we want to clone the repository to that folder
git clone ssh://git@git.example.dev:22448/cis.git /cis
# Execute the setup script
/cis/setupCoreOntoThisHost.sh
```
<br>
<br>
<br>
Setup a new host step by step manually
--------------------------------------
To deploy cis you have to clone this repository to the host as root user.
Therefore you have to set the correct long hostname (fqdn) create a pair of ssh keys (key type ed25519) for user root
and register the SSH public key of root as __deploy key__ to allow readonly access to this repository:
1. First become root:
```sh
sudo -i
```
2. Set the long hostname:
```sh
hostnamectl set-hostname "the-new-unique-long-hostname (fqdn, eg.: host1.example.net)"
```
3. Update Ubuntu:
2. Update Ubuntu:
```sh
# DO NOT SKIP THIS STEP
apt update; apt upgrade -y
```
4. Install git if needed:
3. Install git if needed:
```sh
git --version > /dev/null || apt install git
```
4. Set the long hostname:
```sh
hostnamectl set-hostname "the-new-unique-long-hostname (fqdn, eg.: host1.example.net)"
```
5. If not exist generate the ssh key pair and print the public key of the user root:
```sh
# -t type of the key pair
@@ -41,40 +115,17 @@ We use the modern ed25519 keys, so the public key of root is stored at this loca
|| (ssh-keygen \
-t ed25519 \
-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")
```
This key has to be registerd via gitea web ui as deploy key into the repositories as documented in chapter "Register public host key".
This key has to be registerd via gitea web ui as deploy key into this repository.
### Register public host key
This is an example for `example.net` as domain of the host owner.
1. Repository `iss`, allow __readonly__ access only.
2. Repository `iss-definition-example.net`, allow __readonly__ access only.
3. Repository `iss-state-example.net`, allow __writable__ access.
### Clone the Infrastructure System (iss) repository
After you registered the printed root's public key of this host you can clone the repository and execute the setup script:
```sh
# Note the tailing '/iss', because we want to clone the repository to that folder
git clone ssh://git@git.example.dev:22448/iss.git /iss
# Execute the setup script
/iss/setupCoreOntoThisHost.sh
```
<br>
<br>
<br>
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
- __HTTP-Method:__ POST
- __Trigger On:__ Push Events
@@ -94,11 +145,11 @@ cat "${JENKINS_HOME}/.ssh/id_ed25519.pub" \
|| (ssh-keygen \
-t ed25519 \
-f "${JENKINS_HOME}/.ssh/id_ed25519" -q -N "" \
-C "$(date +%Y%m%d):$(whoami)@$(echo ${JENKINS_URL} | cut -d/ -f3)" \
-C "$(date +%Y%m%d)-$(whoami)@$(echo ${JENKINS_URL} | cut -d/ -f3)" \
&& cat "${JENKINS_HOME}/.ssh/id_ed25519.pub")
# 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/updateRepositories.sh ( --scripts | --definitions | --states ) &
#wait for all background processes to complete
wait

View File

@@ -6,10 +6,10 @@
function checkPermissions(){
local _FOLDER _REPOSITORY
local _FOLDER _RIGHTS
_FOLDER="${1:?"Missing first parameter FOLDER"}"
_RIGHTS="${2:?"Missing second parameter RIGHTS"}"
readonly _FOLDER _REPOSITORY
readonly _FOLDER _RIGHTS
[ "${_RIGHTS}" == "readonly" ] \
&& [ -d "${_FOLDER}/.git" ] \
@@ -21,30 +21,9 @@ function checkPermissions(){
&& git -C "${_FOLDER}" push --dry-run &> /dev/null \
&& return 0
echo "FAIL: The rights of the repository are incorrect: ("$(readlink -f ${0})")"
echo " - '${_FOLDER}' is not '${_RIGHTS}'"
echo " - check the settings of gitea."
return 1
}
function checkRemoteRepository() {
local _FOLDER _REPOSITORY
_FOLDER="${1:?"Missing first parameter FOLDER"}"
_REPOSITORY="${2:?"Missing second parameter REPOSITORY"}"
readonly _FOLDER _REPOSITORY
#Should exist after successful clone only, therefore the remote repository exists and was accessible.
[ -d "${_FOLDER}/.git" ] \
&& return 0
#Checks if repository exists and is accessible.
! [ -d "${_FOLDER}/.git" ] \
&& git ls-remote "${_REPOSITORY}" \
&& return 0
echo "FAIL: The remote repository is not accessible: ("$(readlink -f ${0})")"
echo " - '${_REPOSITORY}'"
echo " - check the settings of gitea."
echo "FAIL: The rights of the repository are incorrect: ("$(readlink -f ${0})")" >&2
echo " - '${_FOLDER}' is not '${_RIGHTS}'" >&2
echo " - check the settings of gitea." >&2
return 1
}
@@ -54,42 +33,67 @@ function cloneOrPull {
_REPOSITORY="${2:?"Missing second parameter REPOSITORY"}"
readonly _FOLDER _REPOSITORY
! [ -d "${_FOLDER}/.git" ] \
&& git clone "${_REPOSITORY}" "${_FOLDER}" &> /dev/null \
&& return 0
[ -d "${_FOLDER}/.git" ] \
&& git -C "${_FOLDER}" pull &> /dev/null \
&& return 0
echo "FAIL: The local repository is not updatable: ("$(readlink -f ${0})")"
echo " - '${_FOLDER}'"
echo " - check your network and the permissions in gitea."
! [ -d "${_FOLDER}/.git" ] \
&& git clone "${_REPOSITORY}" "${_FOLDER}" &> /dev/null \
&& return 0
echo "FAIL: The local repository is not updatable: ("$(readlink -f ${0})")" >&2
echo " - '${_FOLDER}'" >&2
echo " - check your network and the permissions in gitea." >&2
return 1
}
function printRepository(){
local _FOLDER _CONFIGURED_REPOSITORY _SUGGESTED_REPOSITORY
_FOLDER="${1:?"Missing first parameter FOLDER"}"
_CONFIGURED_REPOSITORY="$(git -C "${_FOLDER:?"Missing FOLDER"}" config --get remote.origin.url 2> /dev/null)"
_SUGGESTED_REPOSITORY="${2}"
readonly _FOLDER _CONFIGURED_REPOSITORY _SUGGESTED_REPOSITORY
! [ -z "${_CONFIGURED_REPOSITORY}" ] \
&& echo "${_CONFIGURED_REPOSITORY}" \
&& return 0
while true; do
read -e -p "Enter ssh URL to clone Repository: " -i "${_SUGGESTED_REPOSITORY}" _REPOSITORY
echo "${_REPOSITORY}" | grep -F 'git@' &> /dev/null \
&& git ls-remote "${_REPOSITORY}" &> /dev/null \
&& echo "${_REPOSITORY:?"Missing REPOSITORY: e.g. ssh://git@your.domain.com/cis.git"}" \
&& return 0
done
echo "FAIL: The remote repository is not accessible: ("$(readlink -f ${0})")" >&2
echo " - '${_REPOSITORY}'" >&2
echo " - check the settings of gitea." >&2
return 1
}
# Note that an unprivileged user can use this script successfully,
# if no user has to be added to the host because it already exists.
function addAndCheckGitRepository() {
local _FOLDER _REPOSITORY
local _FOLDER _REPOSITORY _RIGHTS
_FOLDER="${1:?"Missing first parameter FOLDER"}"
_REPOSITORY="${2:?"Missing second parameter REPOSITORY: e.g. ssh://git@your.domain.com/iss.git "}"
_RIGHTS="${3:?"Missing third parameter RIGHTS: (readonly, writable) "}"
readonly _FOLDER _REPOSITORY
_RIGHTS="${2:?"Missing second parameter RIGHTS: (readonly, writable) "}"
_REPOSITORY="$(printRepository "${_FOLDER}" "${3}")"
readonly _FOLDER _REPOSITORY _RIGHTS
checkRemoteRepository "${_FOLDER}" "${_REPOSITORY}" \
&& cloneOrPull "${_FOLDER}" "${_REPOSITORY}" \
echo \
&& cloneOrPull "${_FOLDER}" "${_REPOSITORY:?"Missing REPOSITORY: e.g. ssh://git@your.domain.com/cis.git"}" \
&& checkPermissions "${_FOLDER}" "${_RIGHTS}" \
&& echo "SUCCESS: The git repository is usable. ("$(readlink -f ${0})")" \
&& echo " - remote repository: '${_REPOSITORY}'" \
&& echo " - local repository: '${_FOLDER}' (${_RIGHTS})" \
&& return 0
echo "FAIL: The repository is not functional: ("$(readlink -f ${0})")"
echo " - remote repository: '${_REPOSITORY}'"
echo " - local repository: '${_FOLDER}'"
echo " - due to an error or insufficient rights or"
echo " - one check failed."
echo "FAIL: The repository is not functional: ("$(readlink -f ${0})")" >&2
echo " - remote repository: '${_REPOSITORY}'" >&2
echo " - local repository: '${_FOLDER}'" >&2
echo " - due to an error or insufficient rights or" >&2
echo " - one check failed." >&2
return 1
}
@@ -98,4 +102,6 @@ addAndCheckGitRepository \
"$(echo ${1} | 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')" \
&& exit 0 || exit 1
&& exit 0
exit 1

View File

@@ -18,6 +18,8 @@ function addNormalUser() {
&& echo " - '${_USER}'" \
&& return 0
# useradd is a low level utility ... use adduser(8) instead.
# See: https://askubuntu.com/questions/345974/what-is-the-difference-between-adduser-and-useradd
[ "$(id -u)" == "0" ] \
&& adduser --gecos 'Normal user' --disabled-password "${_USER}" \
&& chown -R "${_USER}:${_USER}" "/home/${_USER}" \
@@ -27,13 +29,14 @@ function addNormalUser() {
&& echo " - existing home directories were taken over" \
&& return 0
echo "FAIL: The user could not be created: ("$(readlink -f ${0})")"
echo " - '${_USER}'"
echo " - due to an error or insufficient rights."
echo "FAIL: The user could not be created: ("$(readlink -f ${0})")" >&2
echo " - '${_USER}'" >&2
echo " - due to an error or insufficient rights." >&2
return 1
}
# sanitizes all parameters
addNormalUser \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0 || exit 1
addNormalUser "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0
exit 1

View File

@@ -5,14 +5,17 @@
# Folders always ends with an tailing '/'
_SCRIPT="$(readlink -f "${0}" 2> /dev/null)"
_CIS_ROOT="${_SCRIPT%%/core/*}/" #Removes longest matching pattern '/core/*' from the end
# Note that an unprivileged user can use this script successfully,
# if no user has to be added to the host because it already exists.
function addToCrontabEveryHour() {
local _ROOT _MINUTE_VALUE _STRING
_ROOT="${0%%/core/*}/" #Removes longest matching pattern '/core/*' from the end
local _MINUTE_VALUE _STRING
! [ -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"
readonly _ROOT _MINUTE_VALUE _STRING
readonly _MINUTE_VALUE _STRING
[ "$(id -u)" == "0" ] \
&& crontab -l | grep -qF "${_STRING:?"Missing CRON_STRING"}" \
@@ -21,11 +24,11 @@ function addToCrontabEveryHour() {
&& return 0
[ "$(id -u)" == "0" ] \
&& echo "${_ROOT:?"Missing ROOT"}" | grep "home" &> /dev/null \
&& echo "${_CIS_ROOT:?"Missing CIS_ROOT"}" | grep -F 'home' &> /dev/null \
&& echo "SUCCESS: Although the entry will be skipped: ("$(readlink -f ${0})")" \
&& echo " - '${_STRING}'" \
&& echo " that is because the current environment is:" \
&& echo " - ${_ROOT}" \
&& echo " - ${_CIS_ROOT}" \
&& return 0
[ "$(id -u)" == "0" ] \
@@ -37,9 +40,9 @@ function addToCrontabEveryHour() {
&& echo " - '${_STRING}'" \
&& return 0
echo "FAIL: Entry could not be registered to crontab: ("$(readlink -f ${0})")"
echo " - '${_STRING:?"Missing CRON_STRING"}'"
echo " - due to an error or insufficient rights."
echo "FAIL: Entry could not be registered to crontab: ("$(readlink -f ${0})")" >&2
echo " - '${_STRING:?"Missing CRON_STRING"}'" >&2
echo " - due to an error or insufficient rights." >&2
return 1
}
@@ -47,4 +50,6 @@ function addToCrontabEveryHour() {
addToCrontabEveryHour \
"$(echo ${1} | 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,6 @@
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

View File

@@ -44,24 +44,24 @@ function prepareFolder() {
&& echo " - '${_SSH_FOLDER}'" \
&& return 0
echo "FAIL: The ssh folder could not be prepared: ("$(readlink -f ${0})")"
echo " - '${_SSH_FOLDER}'"
echo " - due to an error or insufficient rights."
echo "FAIL: The ssh folder could not be prepared: ("$(readlink -f ${0})")" >&2
echo " - '${_SSH_FOLDER}'" >&2
echo " - due to an error or insufficient rights." >&2
return 1
}
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'"}")"
_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="${_DOMAIN%/}" #Removes shortest matching pattern '/' from the end
#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"}"
_CORE_SCRIPTS="${_ROOT:?"Missing ROOT"}core/"
readonly _ROOT _CORE_SCRIPTS _DOMAIN _DEFINITIONS _USER
_CORE_SCRIPTS="${_CIS_ROOT:?"Missing ROOT"}core/"
readonly _CIS_ROOT _CORE_SCRIPTS _DOMAIN _DEFINITIONS _USER
case "${_USER:?"Missing USER"}" in
root)
@@ -83,4 +83,6 @@ function defineAuthorizedKeysOfUser() {
defineAuthorizedKeysOfUser \
"$(echo ${1} | 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

@@ -12,46 +12,76 @@ function printIfEqual() {
}
function isCoreDefinition() {
echo "${1:?"Missing first parameter FILE"}" | grep "/root/.ssh/authorized_keys" &> /dev/null \
echo "${1:?"Missing first parameter FILE"}" | grep -F '/root/.ssh/authorized_keys' &> /dev/null \
&& return 0
echo "${1:?"Missing first parameter FILE"}" | grep "/home/jenkins/.ssh/authorized_keys" &> /dev/null \
echo "${1:?"Missing first parameter FILE"}" | grep -F '/home/jenkins/.ssh/authorized_keys' &> /dev/null \
&& return 0
echo "${1:?"Missing first parameter FILE"}" | grep "/etc/sudoers.d/allow-jenkins-updateRepositories" &> /dev/null \
echo "${1:?"Missing first parameter FILE"}" | grep -F '/etc/sudoers.d/allow-jenkins-updateRepositories' &> /dev/null \
&& return 0
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() {
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_THIS_HOST="${1:?"Missing DEFINITIONS"}/core/$(hostname -s)${2:?"Missing CURRENT_FULLFILE"}"
_FILE_DEFINED_ALL_HOSTS="${1:?"Missing DEFINITIONS"}/hosts/all${2:?"Missing CURRENT_FULLFILE"}"
_FILE_DEFINED_THIS_HOST="${1:?"Missing DEFINITIONS"}/hosts/$(hostname -s)${2:?"Missing CURRENT_FULLFILE"}"
readonly _CORE_FILE_DEFINED_ALL_HOSTS _CORE_FILE_DEFINED_THIS_HOST _FILE_DEFINED_ALL_HOSTS _FILE_DEFINED_THIS_HOST
local _DEFINITIONS _CORE_FILE_DEFINED_ALL_HOSTS _CORE_FILE_DEFINED_THIS_HOST _FILE_DEFINED_ALL_HOSTS _FILE_DEFINED_THIS_HOST
_DEFINITIONS="${1:?"Missing CIS_ROOT"}definitions/${2:?"Missing DOMAIN"}/"
_CORE_DEFAULT_ALL_HOSTS="${1:?"Missing CIS_ROOT"}core/default${3:?"Missing CURRENT_FULLFILE"}"
_CORE_FILE_DEFINED_ALL_HOSTS="${_DEFINITIONS:?"Missing DEFINITIONS"}core/all${3:?"Missing CURRENT_FULLFILE"}"
_CORE_FILE_DEFINED_THIS_HOST="${_DEFINITIONS:?"Missing DEFINITIONS"}core/$(hostname -s)${3:?"Missing CURRENT_FULLFILE"}"
_FILE_DEFINED_ALL_HOSTS="${_DEFINITIONS:?"Missing DEFINITIONS"}hosts/all${3:?"Missing CURRENT_FULLFILE"}"
_FILE_DEFINED_THIS_HOST="${_DEFINITIONS:?"Missing DEFINITIONS"}hosts/$(hostname -s)${3:?"Missing CURRENT_FULLFILE"}"
readonly _DEFINITIONS _CORE_FILE_DEFINED_ALL_HOSTS _CORE_FILE_DEFINED_THIS_HOST _FILE_DEFINED_ALL_HOSTS _FILE_DEFINED_THIS_HOST
#The following are special definitions that affect the core functionality.
#Try this host first because it should be priorized.
isCoreDefinition "${2:?"Missing CURRENT_FULLFILE"}" \
isCoreDefinition "${3:?"Missing CURRENT_FULLFILE"}" \
&& [ -s "${_CORE_FILE_DEFINED_THIS_HOST}" ] \
&& echo "${_CORE_FILE_DEFINED_THIS_HOST}" \
&& filterInvalidAuthorizedKeysFilesOfRoot "${_CORE_FILE_DEFINED_THIS_HOST}" \
&& return 0
#The following are special definitions that affect the core functionality.
isCoreDefinition "${2:?"Missing CURRENT_FULLFILE"}" \
isCoreDefinition "${3:?"Missing CURRENT_FULLFILE"}" \
&& [ -s "${_CORE_FILE_DEFINED_ALL_HOSTS}" ] \
&& echo "${_CORE_FILE_DEFINED_ALL_HOSTS}" \
&& filterInvalidAuthorizedKeysFilesOfRoot "${_CORE_FILE_DEFINED_ALL_HOSTS}" \
&& return 0
#The following are special definitions that affect the core functionality.
isCoreDefinition "${3:?"Missing CURRENT_FULLFILE"}" \
&& [ -s "${_CORE_DEFAULT_ALL_HOSTS}" ] \
&& filterInvalidAuthorizedKeysFilesOfRoot "${_CORE_DEFAULT_ALL_HOSTS}" \
&& return 0
#Try this host first because it should be priorized.
! isCoreDefinition "${2:?"Missing CURRENT_FULLFILE"}" \
! isCoreDefinition "${3:?"Missing CURRENT_FULLFILE"}" \
&& [ -s "${_FILE_DEFINED_THIS_HOST}" ] \
&& echo "${_FILE_DEFINED_THIS_HOST}" \
&& return 0
! isCoreDefinition "${2:?"Missing CURRENT_FULLFILE"}" \
! isCoreDefinition "${3:?"Missing CURRENT_FULLFILE"}" \
&& [ -s "${_FILE_DEFINED_ALL_HOSTS}" ] \
&& echo "${_FILE_DEFINED_ALL_HOSTS}" \
&& return 0
@@ -71,11 +101,6 @@ function createSymlinkToDefinition() {
&& [ "$(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}" ] \
&& [ "$(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}" ] \
&& mv "${_CURRENT_FULLFILE:?"Missing CURRENT_FULLFILE"}" "${_SAVED_FULLFILE:?"Missing SAVED_FULLFILE"}" \
&& echo "Current file has been backed up to: '${_SAVED_FULLFILE}'"
@@ -92,17 +117,17 @@ function createSymlinkToDefinition() {
}
function ensureUsageOfDefinitions() {
local _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'"}")"
_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end
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'"}")/"
_CIS_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end
_DOMAIN="${_DEFINITIONS##*/definitions/}" #Removes longest matching pattern '*/definitions/' from the begin
_DOMAIN="${_DOMAIN%/}" #Removes shortest matching pattern '/' from the end
#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="${_CURRENT_FOLDER%/}/" #Removes shortest matching pattern '/' from the end
_CURRENT_FULLFILE="${2:?"Missing second parameter CURRENT_FULLFILE"}"
_CURRENT_FOLDER="${_CURRENT_FULLFILE%/*}/" #Removes shortest matching pattern '/*' from the end
! [ -d "${_CURRENT_FOLDER}" ] \
&& echo "FAIL: The folder cannot be read: ("$(readlink -f ${0})")" \
&& echo " - '${_CURRENT_FOLDER}'" \
@@ -118,10 +143,16 @@ function ensureUsageOfDefinitions() {
_CURRENT_FULLFILE="${_CURRENT_FOLDER:?"Missing CURRENT_FOLDER"}${_CURRENT_FILE:?"Missing CURRENT_FILE"}"
_DEFINED_FULLFILE="$(printSelectedDefinition "${_DEFINITIONS}" "${_CURRENT_FULLFILE}")"
_DEFINED_FULLFILE="$(printSelectedDefinition "${_CIS_ROOT}" "${_DOMAIN}" "${_CURRENT_FULLFILE}")"
_NOW="$(date +%Y%m%d_%H%M)"
_SAVED_FULLFILE="${_CURRENT_FULLFILE}-backup@${_NOW:?"Missing NOW"}"
readonly _ROOT _CURRENT_FILE _CURRENT_FOLDER _CURRENT_FULLFILE _DEFINITIONS _DOMAIN _DEFINED_FULLFILE _NOW _SAVED_FULLFILE
_SAVED_FULLFILE="${_CURRENT_FULLFILE}.backup@${_NOW:?"Missing NOW"}"
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}" ] \
&& echo "FAIL: No definition available for this file: ("$(readlink -f ${0})")" \
@@ -138,11 +169,11 @@ function ensureUsageOfDefinitions() {
&& echo " - '${_DEFINED_FULLFILE}'" \
&& return 0
echo "${_ROOT:?"Missing ROOT"}" | grep "home" &> /dev/null \
echo "${_CIS_ROOT:?"Missing CIS_ROOT"}" | grep -F 'home' &> /dev/null \
&& echo "SUCCESS: Although this definition will be skipped: ("$(readlink -f ${0})")" \
&& echo " - '${_DEFINED_FULLFILE}'" \
&& echo " that is because the current environment is:" \
&& echo " - ${_ROOT}" \
&& echo " - ${_CIS_ROOT}" \
&& echo " following file is in use:" \
&& echo " - $(readlink -f "${_CURRENT_FULLFILE}")" \
&& return 0
@@ -165,8 +196,8 @@ function ensureUsageOfDefinitions() {
&& echo "- '${_DEFINED_FULLFILE}'" \
&& return 0
echo "FAIL: The definition could not be ensured: ("$(readlink -f ${0})")"
echo " - due to an error or insufficient rights."
echo "FAIL: The definition could not be ensured: ("$(readlink -f ${0})")" >&2
echo " - due to an error or insufficient rights." >&2
return 1
}
@@ -174,4 +205,6 @@ function ensureUsageOfDefinitions() {
ensureUsageOfDefinitions \
"$(echo ${1} | 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

13
core/printCisRoot.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
_SCRIPT="$(readlink -f "${0}" 2> /dev/null)"
_CIS_ROOT="${_SCRIPT%%/core/*}/" #Removes longest matching pattern '/core/*' from the end
[ -d "${_CIS_ROOT}" ] \
&& [ -d "${_CIS_ROOT}definitions/" ] \
&& [ -d "${_CIS_ROOT}states/" ] \
&& echo "${_CIS_ROOT}" \
&& exit 0
echo "FAIL: Unable to detect CIS_ROOT" >&2
exit 1

30
core/printOwnDomain.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
#WARNING: Used for core functionality in setup.sh
# DO NOT rename the script and test changes well!
# Folders always ends with an tailing '/'
_SCRIPT="$(readlink -f "${0}" 2> /dev/null)"
_CIS_ROOT="${_SCRIPT%%/core/*}/" #Removes longest matching pattern '/core/*' from the end
_OVERRIDE_DOMAIN_FILE="${_CIS_ROOT:?"Missing CIS_ROOT"}overrideOwnDomain"
# There has to be one dot at least.
_BOOT_DOMAIN="$(hostname -b | grep -F '.' | cut -d. -f2-)"
# Take OVERRIDING_DOMAIN_FILE without empty lines and comments, then take the first line without leading spaces
_OVERRIDE_DOMAIN="$(grep -vE '^[[:space:]]*$|^[[:space:]]*#' "${_OVERRIDE_DOMAIN_FILE}" 2> /dev/null | head -n 1 | xargs)"
! [ -z "${_OVERRIDE_DOMAIN}" ] \
&& [ "${_OVERRIDE_DOMAIN}" != "${_BOOT_DOMAIN}" ] \
&& echo "WARNING: Domain has been overridden by: ${_OVERRIDE_DOMAIN_FILE}" >&2 \
&& echo "${_OVERRIDE_DOMAIN}" \
&& exit 0
! [ -z "${_BOOT_DOMAIN}" ] \
&& echo "${_BOOT_DOMAIN}" \
&& exit 0
echo "It was impossible to find out the domain of this host, please prepare this host first." >&2
exit 1

69
prepareDefinitionsRepository.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/bash
[ "$(id -u)" != "0" ] \
&& echo "This script prepares the user 'root' of this host and the host itself," \
&& echo "so this script is allowed to be executed if you are root only." \
&& exit 1
# There has to be one dot at least.
_BOOT_DOMAIN="$(hostname -b | grep -F '.' | cut -d. -f2-)"
[ -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
This repository contains the definitions of the domain “$_BOOT_DOMAIN” by the Core Infrastructure System.
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
#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
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'
Please DO NOT use the SSH key of root for this.
Maybe you can use https and user password for pushing the first commit.
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:
cd /tmp/skeleton/definition
git init
git checkout -b main
git add .
git commit -m "first core definitions"
git remote add origin https://git.example.dev/[SOME_PATH/]$_REOPSITORY_NAME.git
git push -u origin main
EOF

48
prepareStatesRepository.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/bin/bash
[ "$(id -u)" != "0" ] \
&& echo "This script prepares the user 'root' of this host and the host itself," \
&& echo "so this script is allowed to be executed if you are root only." \
&& exit 1
# There has to be one dot at least.
_BOOT_DOMAIN="$(hostname -b | grep -F '.' | cut -d. -f2-)"
[ -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
This repository contains the states of the domain “$_BOOT_DOMAIN” by the Core Infrastructure System.
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'
Please DO NOT use the SSH key of root for this.
Maybe you can use https and user password for pushing the first commit.
Then go to folder '/tmp/skeleton/state' and follow the instructions as your git server shows.
For example:
cd /tmp/skeleton/state
git init
git checkout -b main
git add .
git commit -m "first state"
git remote add origin https://git.example.dev/[SOME_PATH/]$_REOPSITORY_NAME.git
git push -u origin main
EOF

View File

@@ -8,46 +8,66 @@
function setNeededHostnameOrExit() {
_FQDN="${1:?"Missing unique long hostname (fqdn, eg.: host1.example.net) for this host as first parameter."}"
echo "${_FQDN}" | grep '\.' &> /dev/null \
echo "${_FQDN}" | grep -F '.' &> /dev/null \
&& hostnamectl set-hostname "${_FQDN}" \
&& return 0
echo "FAILED: setting full qualified domain name, given value was:"
echo " - ${_FQDN}"
echo "FAILED: setting full qualified domain name does not contain a domain,"
echo " given value was: ${_FQDN}"
exit 1
}
function prepare() {
function prepareThisHost() {
git --version > /dev/null || (apt update; apt upgrade -y; apt install git)
echo
echo "Public SSH-Key for root@$(hostname -b):"
cat "/root/.ssh/id_ed25519.pub" \
&& return 0
# -t type of the key pair
# -f defines the filenames (we use the standard for the selected type here)
# -q quiet, no output or interaction
# -N "" means the private key will not be secured by a passphrase
# -C defines a comment
cat "/root/.ssh/id_ed25519.pub" \
|| (ssh-keygen \
ssh-keygen \
-t ed25519 \
-f "/root/.ssh/id_ed25519" -q -N "" \
-C "$(date +%Y%m%d)-root@$(hostname -b)" \
&& cat "/root/.ssh/id_ed25519.pub")
-C "$(date +%Y%m%d)-root@$(hostname -b)"
cat "/root/.ssh/id_ed25519.pub" \
&& return 0
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 " - definitions repository (allow readonly access only),"
echo " - states repository (allow writable access)."
echo
echo "After all access rights are granted you can clone the Infrastructure System:"
echo " e.g.: git clone ssh://git@git.example.dev:22448/iss.git /iss"
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/cis.git /cis"
echo
echo "Finally call 'setupCoreOntoThisHost.sh' from the root directory of the repository:"
echo " e.g.: /iss/setupCoreOntoThisHost.sh"
echo "Finally call 'setupCoreOntoThisHost.sh' from the root directory:"
echo " e.g.: /cis/setupCoreOntoThisHost.sh"
echo
}
# sanitizes all parameters
setNeededHostnameOrExit "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& prepare
&& prepareThisHost \
&& showFurtherSteps \
&& exit 0
exit 1

View File

@@ -6,6 +6,12 @@
# Folders always ends with an tailing '/'
_SETUP="$(readlink -f "${0}" 2> /dev/null)"
_CIS_ROOT="${_SETUP%/setupCoreOntoThisHost.sh}/" #Removes shortest matching pattern '/setupCoreOntoThisHost.sh' from the end
_CORE_SCRIPTS="${_CIS_ROOT:?"Missing CIS_ROOT"}core/"
function checkPathsAreAvaiable() {
grep --version &> /dev/null \
@@ -31,22 +37,21 @@ function checkGitIsAvailable() {
}
function checkPreconditions() {
local _ROOT _DOMAIN
_ROOT="${1:?"Missing parameter ROOT"}"
_DOMAIN="${2}" # Optional parameter DOMAIN
readonly _ROOT _DOMAIN
local _DOMAIN
_DOMAIN="${1}" # Optional parameter DOMAIN
readonly _DOMAIN
! [ -z "${_DOMAIN}" ] \
&& [ "$(hostname -d)" != "${_DOMAIN}" ] \
&& echo \
&& echo "WARNING: system-domain DOES NOT MATCH domainOfHostOwner: '$(hostname -d)' != '${_DOMAIN}'" \
&& echo "WARNING: system-domain DOES NOT MATCH overrideOwnDomain: '$(hostname -d)' != '${_DOMAIN}'" \
&& echo
# Given domain verfügbar (nicht leer)
! [ -z "${_DOMAIN}" ] \
&& checkPathsAreAvaiable \
&& checkGitIsAvailable \
&& git -C "${_ROOT}" pull &> /dev/null \
&& git -C "${_CIS_ROOT:?"Missing CIS_ROOT"}" pull &> /dev/null \
&& return 0
echo
@@ -69,63 +74,65 @@ function checkPreconditions() {
}
function getOrSetDomain() {
local _ROOT _DOMAIN_FILE _GIVEN_DOMAIN
_ROOT="${1:?"Missing parameter ROOT"}"
_DOMAIN_FILE="${_ROOT:?"Missing ROOT"}domainOfHostOwner"
_GIVEN_DOMAIN="${2}" # Optional parameter DOMAIN
readonly _ROOT _DOMAIN_FILE _GIVEN_DOMAIN
local _CURRENT_DOMAIN _GIVEN_DOMAIN _OVERRIDE_DOMAIN_FILE
_CURRENT_DOMAIN="$("${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}printOwnDomain.sh")"
_GIVEN_DOMAIN="${1}" # Optional parameter DOMAIN
_OVERRIDE_DOMAIN_FILE="${_CIS_ROOT:?"Missing CIS_ROOT"}overrideOwnDomain"
readonly _CURRENT_DOMAIN _GIVEN_DOMAIN _OVERRIDE_DOMAIN_FILE
# Wenn DOMAIN_FILE enhält lesbare Daten
grep '[^[:space:]]' "${_DOMAIN_FILE:?"Missing DOMAIN_FILE"}" &> /dev/null \
&& cat "${_DOMAIN_FILE}" \
! [ -z "${_CURRENT_DOMAIN}" ] \
&& [ -z "${_GIVEN_DOMAIN}" ] \
&& echo "${_CURRENT_DOMAIN}" \
&& return 0
# Der boot-hostname muss mindestens einen Punkt enthalten, dann wird die hintere Hälfte als Domain genommen
hostname -b | grep "\." | cut -d. -f2- > "${_DOMAIN_FILE}"
grep '[^[:space:]]' "${_DOMAIN_FILE}" &> /dev/null \
&& cat "${_DOMAIN_FILE}" \
! [ -z "${_CURRENT_DOMAIN}" ] \
&& [ "${_CURRENT_DOMAIN}" == "${_GIVEN_DOMAIN}" ] \
&& echo "${_CURRENT_DOMAIN}" \
&& return 0
# Given domain is set (nicht leer)
! [ -z "${_GIVEN_DOMAIN}" ] \
# If there is a given domain it will be set or it will override the current one
[ -z "${_CURRENT_DOMAIN}" ] \
&& ! [ -z "${_GIVEN_DOMAIN}" ] \
&& [ "$(id -u)" == "0" ] \
&& echo "Setting hostname to: $(hostname -s).${_GIVEN_DOMAIN}" >&2 \
&& hostnamectl set-hostname "$(hostname -s).${_GIVEN_DOMAIN}" \
&& hostname -b | grep "\." | cut -d. -f2- > "${_DOMAIN_FILE}" \
&& grep '[^[:space:]]' "${_DOMAIN_FILE}" &> /dev/null \
&& cat "${_DOMAIN_FILE}" \
&& echo "${_GIVEN_DOMAIN}" \
&& return 0
! [ -z "${_GIVEN_DOMAIN}" ] \
&& echo "Overwriting domain to: ${_GIVEN_DOMAIN}" >&2 \
&& echo "${_GIVEN_DOMAIN}" > "${_OVERRIDE_DOMAIN_FILE}" \
&& echo "${_GIVEN_DOMAIN}" \
&& return 0
return 1
}
function getRemoteRepositoryPath() {
local _ROOT
_ROOT="${1:?"Missing parameter ROOT"}"
readonly _ROOT
_RESULT="$(git -C "${_ROOT:?"Missing ROOT"}" remote show origin | grep -i 'fetch' | xargs -n 1 | grep -i 'ssh://')"
_RESULT="${_RESULT%/*}" #Removes shortest matching pattern '/*' from the end
! [ -z "${_RESULT}" ] \
&& echo "${_RESULT}" \
_REPOSITORY="$(git -C "${_CIS_ROOT:?"Missing CIS_ROOT"}" config --get remote.origin.url 2> /dev/null | grep -i 'git@')"
_PATH="${_REPOSITORY%/*}" #Removes shortest matching pattern '/*' from the end
! [ -z "${_PATH}" ] \
&& echo "${_PATH}/" \
&& return 0
return 1
}
function addDefinition(){
local _ROOT _CORE_SCRIPTS _DEFINITIONS _REPOSITORY
_DEFINITIONS="${1:?"Missing parameter DEFINITIONS"}"
_REPOSITORY="${2:?"Missing parameter REPOSITORY"}"
_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end
_CORE_SCRIPTS="${_ROOT:?"Missing ROOT"}core/"
readonly _ROOT _CORE_SCRIPTS _DEFINITIONS _REPOSITORY
local _DEFINITIONS _REPOSITORY
_DEFINITIONS="${1:?"Missing first parameter DEFINITIONS"}"
_REPOSITORY="$(getRemoteRepositoryPath)cis-definition-${2:?"Missing second parameter DOMAIN"}.git"
readonly _DEFINITIONS _REPOSITORY
[ "$(id -u)" == "0" ] \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_DEFINITIONS}" "${_REPOSITORY}" readonly \
&& echo "Running setup as 'root' trying to add definition repository:" \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_DEFINITIONS}" readonly "${_REPOSITORY}" \
&& echo " - definitions are usable for this host." \
&& return 0
[ "$(id -u)" != "0" ] \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_DEFINITIONS}" "${_REPOSITORY}" writable \
&& echo "Running setup as 'user' trying to add definition repository:" \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_DEFINITIONS}" writable "${_REPOSITORY}" \
&& echo " - definitions are usable, as working copy." \
&& return 0
@@ -133,20 +140,22 @@ function addDefinition(){
}
function addState() {
local _ROOT _CORE_SCRIPTS _STATES _REPOSITORY
_STATES="${1:?"Missing parameter STATES"}"
_REPOSITORY="${2:?"Missing parameter REPOSITORY"}"
_ROOT="${_STATES%%/states/*}/" #Removes longest matching pattern '/states/*' from the end
_CORE_SCRIPTS="${_ROOT:?"Missing ROOT"}core/"
readonly _ROOT _CORE_SCRIPTS _STATES _REPOSITORY
local _STATES _REPOSITORY
_STATES="${1:?"Missing first parameter STATES"}"
_REPOSITORY="$(getRemoteRepositoryPath)cis-state-${2:?"Missing second parameter DOMAIN"}.git"
readonly _STATES _REPOSITORY
[ "$(id -u)" == "0" ] \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_STATES}" "${_REPOSITORY}" writable \
&& echo "Running setup as 'root' trying to add state repository:" \
&& echo \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_STATES}" writable "${_REPOSITORY}" \
&& echo " - states are usable for this host." \
&& return 0
[ "$(id -u)" != "0" ] \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_STATES}" "${_REPOSITORY}" writable \
&& echo "Running setup as 'user' trying to add state repository:" \
&& echo \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addAndCheckGitRepository.sh" "${_STATES}" writable "${_REPOSITORY}" \
&& echo " - states are usable, as working copy." \
&& return 0
@@ -154,13 +163,10 @@ function addState() {
}
function setupCoreFunctionality() {
local _ROOT _CORE_SCRIPTS _DEFINITIONS _MINUTE_FROM_OWN_IP _SETUP
local _DEFINITIONS _MINUTE_FROM_OWN_IP
_DEFINITIONS="${1:?"Missing DEFINITIONS: 'ROOT/definitions/DOMAIN'"}"
_ROOT="${_DEFINITIONS%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end
_CORE_SCRIPTS="${_ROOT:?"Missing 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
_SETUP="${2:?"Missing SETUP"}"
readonly _ROOT _CORE_SCRIPTS _DEFINITIONS _MINUTE_FROM_OWN_IP _SETUP
_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
readonly _DEFINITIONS _MINUTE_FROM_OWN_IP
[ "$(id -u)" != "0" ] \
&& echo "Configuration of host skipped because of insufficient rights." \
@@ -176,45 +182,39 @@ function setupCoreFunctionality() {
&& echo \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}ensureUsageOfDefinitions.sh" "${_DEFINITIONS}" /etc/sudoers.d/allow-jenkins-updateRepositories \
&& echo \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addToCrontabEveryHour.sh" "${_SETUP}" "${_MINUTE_FROM_OWN_IP}" \
&& "${_CORE_SCRIPTS:?"Missing CORE_SCRIPTS"}addToCrontabEveryHour.sh" "${_SETUP:?"Missing SETUP"}" "${_MINUTE_FROM_OWN_IP}" \
&& return 0
return 1
}
function setup() {
local _ROOT _DEFINITIONS _DEFINITIONS_REPOSITORY _DOMAIN _REPOSITORY_PATH _SETUP _STATES _STATES_REPOSITORY
_SETUP="$(readlink -f "${0}" 2> /dev/null)"
_ROOT="$(dirname ${_SETUP:?"Missing SETUP"} 2> /dev/null || echo "/iss")/"
_DOMAIN="$(getOrSetDomain "${_ROOT:?"Missing ROOT"}" "${1}")"
_REPOSITORY_PATH="$(getRemoteRepositoryPath "${_ROOT:?"Missing ROOT"}")"
local _DEFINITIONS _DOMAIN _STATES
_DOMAIN="$(getOrSetDomain "${1}")"
! checkPreconditions "${_ROOT:?"Missing ROOT"}" "${_DOMAIN}" \
! checkPreconditions "${_DOMAIN}" \
&& return 1
_DEFINITIONS="${_ROOT:?"Missing ROOT"}definitions/${_DOMAIN:?"Missing DOMAIN"}"
_DEFINITIONS_REPOSITORY="${_REPOSITORY_PATH:?"Missing REPOSITORY_PATH"}/iss-definition-${_DOMAIN:?"Missing DOMAIN"}.git"
_STATES="${_ROOT:?"Missing ROOT"}states/${_DOMAIN:?"Missing DOMAIN"}"
_STATES_REPOSITORY="${_REPOSITORY_PATH:?"Missing REPOSITORY_PATH"}/iss-state-${_DOMAIN:?"Missing DOMAIN"}.git"
readonly _ROOT _DEFINITIONS _DEFINITIONS_REPOSITORY _DOMAIN _REPOSITORY_PATH _SETUP _STATES _STATES_REPOSITORY
_DEFINITIONS="${_CIS_ROOT:?"Missing CIS_ROOT"}definitions/${_DOMAIN:?"Missing DOMAIN"}"
_STATES="${_CIS_ROOT:?"Missing CIS_ROOT"}states/${_DOMAIN:?"Missing DOMAIN"}"
readonly _DEFINITIONS _DOMAIN _STATES
echo \
&& echo "Running setup using repositories of: '${_REPOSITORY_PATH:?"Missing REPOSITORY_PATH"}' ..." \
&& addDefinition "${_DEFINITIONS:?"Missing DEFINITIONS"}" "${_DOMAIN:?"Missing DOMAIN"}" \
&& echo \
&& addDefinition "${_DEFINITIONS:?"Missing DEFINITIONS"}" "${_DEFINITIONS_REPOSITORY:?"Missing DEFINITIONS_REPOSITORY"}" \
&& echo \
&& addState "${_STATES:?"Missing STATES"}" "${_STATES_REPOSITORY:?"Missing STATES_REPOSITORY"}" \
&& addState "${_STATES:?"Missing STATES"}" "${_DOMAIN:?"Missing DOMAIN"}" \
&& echo \
&& echo "Using definitions: '${_DEFINITIONS:?"Missing DEFINITIONS"}' ..." \
&& setupCoreFunctionality "${_DEFINITIONS:?"Missing DEFINITIONS"}" "${_SETUP:?"Missing SETUP"}" \
&& setupCoreFunctionality "${_DEFINITIONS:?"Missing DEFINITIONS"}" \
&& return 0
echo "FAIL: setup is incomplete: ("$(readlink -f ${0})")"
echo " - due to an error or insufficient rights."
echo "FAIL: setup is incomplete: ("$(readlink -f ${0})")" >&2
echo " - due to an error or insufficient rights." >&2
return 1
}
# sanitizes all parameters
setup \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0 || exit 1
setup "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0
exit 1

View File

@@ -20,54 +20,61 @@
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)"
_MODE="${1:-"all"}"
_ROOT="$(dirname ${_UPDATE_REPOSITORIES:?"Missing UPDATE_REPOSITORIES"} 2> /dev/null || echo "/iss")/"
_DOMAIN="$(cat ${_ROOT:?"Missing ROOT"}domainOfHostOwner)"
_DEFINITIONS="${_ROOT}definitions/${_DOMAIN:?"Missing DOMAIN from file: ${_ROOT}domainOfHostOwner"}/"
_STATES="${_ROOT}states/${_DOMAIN:?"Missing DOMAIN from file: ${_ROOT}domainOfHostOwner"}/"
readonly _ROOT _DEFINITIONS _DOMAIN _MODE _STATES _UPDATE_REPOSITORIES
_CIS_ROOT="${_UPDATE_REPOSITORIES%/updateRepositories.sh}/" #Removes shortest matching pattern '/updateRepositories.sh' from the end
_MODE="${1:-"--core"}"
_DOMAIN="$(${_CIS_ROOT:?"Missing CIS_ROOT"}core/printOwnDomain.sh)"
_DEFINITIONS="${_CIS_ROOT}definitions/${_DOMAIN:?"Missing DOMAIN from file: ${_CIS_ROOT}domainOfHostOwner"}/"
_STATES="${_CIS_ROOT}states/${_DOMAIN:?"Missing DOMAIN from file: ${_CIS_ROOT}domainOfHostOwner"}/"
readonly _CIS_ROOT _DEFINITIONS _DOMAIN _MODE _STATES _UPDATE_REPOSITORIES
[ "${_MODE}" == "--repair" ] \
&& (git -C "${_ROOT}" reset --hard origin/master; \
git -C "${_DEFINITIONS}" reset --hard origin/master; \
git -C "${_STATES}" reset --hard origin/master; \
&& (git -C "${_CIS_ROOT}" reset --hard origin/main; \
git -C "${_DEFINITIONS}" reset --hard origin/main; \
git -C "${_STATES}" reset --hard origin/main; \
echo "Run repairs") \
&& return 0
[ "${_MODE}" == "--test" ] \
&& git -C "${_ROOT}" pull \
&& git -C "${_CIS_ROOT}" pull \
&& git -C "${_DEFINITIONS}" pull \
&& git -C "${_STATES}" pull \
&& echo "Run in testMode successfully." \
&& return 0
[ "${_MODE}" == "--scripts" ] \
&& echo "Host $HOSTNAME updating scripts: ${_ROOT} ..." \
&& (git -C "${_ROOT}" pull &> /dev/null &) \
&& printf "Host $HOSTNAME updating scripts: ${_CIS_ROOT} ... " \
&& (git -C "${_CIS_ROOT}" pull &> /dev/null) \
&& echo "(done)" \
&& return 0
[ "${_MODE}" == "--definitions" ] \
&& echo "Host ${HOSTNAME} updating definitions: ${_DEFINITIONS} ..." \
&& (git -C "${_DEFINITIONS}" pull &> /dev/null &) \
&& echo "Host ${HOSTNAME} updating definitions: ${_DEFINITIONS} ... " \
&& (git -C "${_DEFINITIONS}" pull &> /dev/null) \
&& echo "(done)" \
&& return 0
[ "${_MODE}" == "--states" ] \
&& echo "Host ${HOSTNAME} updating states: ${_STATES} ..." \
&& (git -C "${_STATES}" pull &> /dev/null &) \
&& echo "Host ${HOSTNAME} updating states: ${_STATES} ... " \
&& (git -C "${_STATES}" pull &> /dev/null) \
&& echo "(done)" \
&& return 0
echo "Host ${HOSTNAME} updating ${_MODE}:" \
&& echo " - ${_ROOT}" \
&& echo " - ${_DEFINITIONS}" \
&& echo " - ${_STATES}"
git -C "${_ROOT}" pull &> /dev/null
git -C "${_DEFINITIONS}" pull &> /dev/null
git -C "${_STATES}" pull &> /dev/null
[ "${_MODE}" == "--core" ] \
&& echo "Host ${HOSTNAME} updating core including scripts, definitions and states: ${_STATES} ... " \
&& (git -C "${_CIS_ROOT}" pull &> /dev/null) \
&& (git -C "${_DEFINITIONS}" pull &> /dev/null) \
&& (git -C "${_STATES}" pull &> /dev/null) \
&& echo "(done)" \
&& return 0
echo "FAILED: an error occurred during an update."
return 1
}
# sanitizes all parameters
update_repositories \
"$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0 || exit 1
update_repositories "$(echo ${1} | sed -E 's|[^a-zA-Z0-9/:@._-]*||g')" \
&& exit 0
exit 1