diff --git a/init.ps1 b/init.ps1 new file mode 100644 index 0000000..aeedc76 --- /dev/null +++ b/init.ps1 @@ -0,0 +1,33 @@ +echo "" +echo "" +echo "" +echo "==============================" +echo "" +echo "Waren Init" +echo "" +echo "==============================" +echo "" +echo "" +echo "Generating SSH Key" +New-Item -ItemType Directory -Path "$Env:USERPROFILE\.ssh\keys" -Force +ssh-keygen -f "$Env:USERPROFILE\.ssh\keys\infra" -t ed25519 -C "$Env:USERDOMAIN" +echo "" +echo "" +echo "Copy SSH Key" +Get-Content "$Env:USERPROFILE\.ssh\keys\infra.pub" +echo "" +echo "" +echo "Install OpenSSH Server" +Add-WindowsCapability -Online -Name OpenSSH.Server +echo "" +echo "" +echo "Enable OpenSSH Server" +Set-Service -Name sshd -StartupType 'Automatic' +echo "" +echo "" +echo "Start OpenSSH Server" +Start-Service -Name sshd +echo "" +echo "" +echo "Install Debian" +wsl --install Debian diff --git a/init.sh b/init.sh new file mode 100755 index 0000000..8b1cc12 --- /dev/null +++ b/init.sh @@ -0,0 +1,594 @@ +#!/bin/bash +if [ ! "$BASH_VERSION" ] ; then + echo "Warén Group / Infra / Init" + echo "This command requires using BASH shell." + exit 1 +fi + +if [[ -L "$0" ]] +then + PWD=$(dirname -- "$(readlink -f -- "$0")") +fi + +wxi-args(){ + declare -gAx args + args=() + + while [[ $# -gt 0 ]]; do + case "$1" in + --*=*) + key="${1%%=*}" + value="${1#*=}" + args["${key#--}"]="$value" + shift + ;; + + --*) + key="$1" + opt="${key#--}" + shift + if [[ $# -gt 0 && "${1}" != -* ]]; then + args["$opt"]="$1" + shift + else + args["$opt"]=true + fi + ;; + + -*=*) + key="${1%%=*}" + value="${1#*=}" + args["${key#-}"]="$value" + shift + ;; + + -*) + key="$1" + opt="${key#-}" + shift + if [[ $# -gt 0 && "${1}" != -* ]]; then + args["$opt"]="$1" + shift + else + args["$opt"]=true + fi + ;; + + *) + args["$((++pos))"]="${1%%=*}" + shift + ;; + esac + done +} + +wxi-config(){ + declare -gAx config + case $1 in + create) + if [[ ! -f $HOME/.warengroup/config.json ]] + then + echo '{}' > $HOME/.warengroup/config.json + fi + ;; + deploy) + if [[ ! $(jq -e . $HOME/.warengroup/config.json.tmp >/dev/null 2>&1) ]] + then + mv $HOME/.warengroup/config.json.tmp $HOME/.warengroup/config.json &> /dev/null + fi + ;; + write) + jq '.'"$2"' = "'$3'"' $HOME/.warengroup/config.json &> $HOME/.warengroup/config.json.tmp + wxi-config deploy + ;; + load) + while IFS=$'\t' read -r k v; do config["$k"]="$v"; done < <( + jq -r ' + paths(scalars) as $p + | "\( $p|map(tostring)|join(".") )\t\( getpath($p) )" + ' $HOME/.warengroup/config.json + ) + ;; + reload) + wxi-config load + ;; + *) + WX_HOME=$HOME/.warengroup + export WX_HOME + + mkdir -p $WX_HOME + chmod 700 -R "$WX_HOME" + + mkdir -p "$WX_HOME/credentials" \ + "$WX_HOME/credentials/ssh" \ + "$WX_HOME/credentials/ssh/eas" \ + "$WX_HOME/credentials/ansible-vault/eas" \ + "$WX_HOME/infra/eas/credentials/ssh" + + find "$WX_HOME" -type d -exec chmod 700 {} + + find "$WX_HOME" -type f -exec chmod 600 {} + + + wxi-config create + if [[ -n ${args['proxy']} ]] + then + wxi-config write proxy "${args['proxy']}" + fi + + wxi-config load + + PROXY_ARGS=() + if [[ -n "${config['proxy']}" ]]; then + PROXY_ARGS=(--proxy "${config['proxy']}") + fi + ;; + esac +} + +wxi-config-legacy(){ + case $1 in + login) + case $2 in + write) + jq '.login.'$ORG'.token = "'$TOKEN'"' $WX_HOME/config.json &> $WX_HOME/config.json.tmp + ;; + read) + if [[ -f "$WX_HOME/config.json" && $(cat $WX_HOME/config.json | jq -r .login.$ORG.token) != 'null' && $(cat $WX_HOME/config.json | jq -r .login.$ORG.token) != '' ]] + then + cat $WX_HOME/config.json | jq -r .login.$ORG.token + else + return 1 + fi + ;; + erase) + TOKEN="" + wxi-config-legacy login write + ;; + esac + ;; + *) + echo -n "" + ;; + esac + + mv $WX_HOME/config.json.tmp $WX_HOME/config.json &> /dev/null +} + +wxi-restricted(){ + if [[ -z $1 || $1 == "--user" ]] + then + if [[ $USER == "root" || $USER == "local" ]] + then + wxi-content status "Command" "Restricted" + wxi-content text "It's not permitted to execute this command with root or local user." + wxi-repeat "\n" 3 + exit 1 + fi + fi + + if [[ $1 == "--org" ]] + then + if [[ ! -z ${args['org']} ]] + then + case ${args['org']} in + warengroup) + ORG=warengroup + ;; + cwchristerw) + ORG=cwchristerw + ;; + *) + wxi-content status "Organization" "Unsupported" + wxi-stop + ;; + esac + elif [[ $(hostname -d) == "devices.waren.io" ]] + then + ORG=warengroup + elif [[ $(hostname -d) == "devices.christerwaren.fi" ]] + then + ORG=cwchristerw + fi + + if [[ ! -z $ORG ]] + then + case $ORG in + warengroup) + DOMAIN=waren.io + VAULT_DOMAIN=vault.cwinfo.net + ORG_HEADER="Warén Group" + ;; + cwchristerw) + DOMAIN=christerwaren.fi + VAULT_DOMAIN=vault.cwinfo.net + ORG_HEADER="Christer Warén" + ;; + *) + wxi-content status "Organization" "Unsupported" + wxi-stop + ;; + esac + else + wxi-content status "Organization" "Required" + wxi-stop + fi + fi + + if [[ -z $1 || $1 == "--vault" ]] + then + if [[ -z $VAULT_DOMAIN ]] + then + wxi-content status "Vault" "Unavailable" + wxi-repeat "\n" 3 + exit 1 + fi + + VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/sys/health) + if [[ ! $VAULT_STATUS -eq 200 ]] + then + wxi-content status "Vault" "Offline" + wxi-repeat "\n" 3 + exit 1 + fi + fi +} + +wxi-init(){ + wx-login + wxi-header "Init" + + if [[ ! -z ${args['hostname']} && ${#args['hostname']} -gt 5 ]] + then + HOSTNAME="${args['hostname']}.devices.$DOMAIN" + elif [[ $(hostname -d) ]] + then + HOSTNAME=$(hostname --fqdn) + else + wxi-content status "Hostname" "Required" + wxi-stop + fi + + if [[ ! -z ${args['device-type']} ]] + then + if [[ ${args['device-type']} == "server" ]] + then + DEVICE_TYPE="Server" + elif [[ ${args['device-type']} == "workstation" ]] + then + DEVICE_TYPE="Workstation" + else + wxi-content status "Device Type" "Invalid" + wxi-stop + fi + else + wxi-content status "Device Type" "Required" + wxi-stop + fi + + mkdir -p ~/.ssh/keys &> /dev/null + + apt-get update &> /dev/null + apt-get install -y python3-pip python3-venv jq git curl lsb-release &> /dev/null + dnf install -y epel-release &> /dev/null + dnf install -y python3-pip jq git curl lsb_release &> /dev/null + python3 -m venv /opt/ansible &> /dev/null + /opt/ansible/bin/pip3 install ansible &> /dev/null + /opt/ansible/bin/pip3 install cryptography dnspython hvac jmespath netaddr passlib pexpect xmltodict &> /dev/null + + curl \ + -H "X-Vault-Token: $TOKEN" \ + -X GET \ + https://$VAULT_DOMAIN/v1/init.sh/data/ssh -s | jq -r '.data.data.privkey' > ~/.ssh/keys/init + + chmod 700 ~/.ssh/keys/init &> /dev/null + + mkdir -p ~/.ansible &> /dev/null + + if [ ! -f ~/.ansible/vars.yml ] + then + echo "---" > ~/.ansible/vars.yml + echo "hostname: $HOSTNAME" >> ~/.ansible/vars.yml + echo "info:" >> ~/.ansible/vars.yml + echo " type: $DEVICE_TYPE" >> ~/.ansible/vars.yml + echo "config:" >> ~/.ansible/vars.yml + echo " identity:" >> ~/.ansible/vars.yml + echo " vault:" >> ~/.ansible/vars.yml + echo " domain: $VAULT_DOMAIN" >> ~/.ansible/vars.yml + fi + + GIT_DOMAIN="git.waren.io" + GIT_PORT="2222" + GIT_REPOSITORY="warengroup-private/infra-plus" + + export HVT=$TOKEN + + /opt/ansible/bin/ansible-galaxy collection install ansible.mysql ansible.posix ansible.utils ansible.windows community.crypto community.dns community.docker community.general community.grafana community.hashi_vault community.libvirt community.mongodb community.postgresql community.windows containers.podman --upgrade &> /dev/null + + /opt/ansible/bin/ansible-pull -U ssh://git@$GIT_DOMAIN:$GIT_PORT/$GIT_REPOSITORY -d ~/.ansible/pull/infra --accept-host-key --private-key ~/.ssh/keys/init --extra-vars @~/.ansible/vars.yml init.yml -t init + + unset HVT +} + +wxi-start(){ + wxi-header "Warén Group / Infra / Init" h1 + + mkdir -p $HOME/.warengroup &> /dev/null + + if [[ ! -f "$HOME/.warengroup/config.json" || $(jq -e . < $HOME/.warengroup/config.json &>/dev/null; echo $?) -gt 0 ]] + then + echo '{}' | jq > $HOME/.warengroup/config.json + fi + + mkdir -p $HOME/.ssh/keys + chmod 700 -R $HOME/.ssh/keys + + echo -n "$wxiBold" + echo "Designed by" + echo -n "$wxiNormal" + wxiLink "Warén Group™" "https://waren.io" +} + +wxi-stop (){ + rm -rf /.ssh/keys/init &> /dev/null + + wxi-repeat "\n" 3 + exit 1 +} + +wxi-content(){ + if [[ $1 == "text" ]] + then + echo "$2" + elif [[ $1 == "text*" ]] + then + echo -n "$2" + elif [[ $1 == "status" ]] + then + echo -n "Status: " + echo "$2 - $3" + elif [[ $1 == "link" ]] + then + echo "$2 - $3" + fi +} + +wxiRed=$(tput setaf 196) +wxiGreen=$(tput setaf 46) +wxiYellow=$(tput setaf 226) +wxiBlue=$(tput setaf 21) +wxiPurple=$(tput setaf 165) +wxiTurquoise=$(tput setaf 14) +wxiPink=$(tput setaf 198) +wxiOrange=$(tput setaf 202) +wxiUnderline=$(tput smul) +wxiBold=$(tput bold) +wxiNormal=$(tput sgr0) + +wxiLink() { + printf '\e]8;;%s\e\\%s\e]8;;\e\\\n' "$2" "$1" + echo "" +} + +wxi-header(){ + if [[ $2 == "h1" ]] + then + #wxi-repeat " " $(($(tput cols)/2-${#1}/2)) + echo -n "$wxiBold" + echo ${1^^} + echo -n "$wxiNormal" + echo "" + fi + + if [[ $2 == "h1*" ]] + then + #wxi-repeat " " $(($(tput cols)/2-${#1}/2)) + echo -n "$wxiBold" + echo ${1^^} + echo -n "$wxiNormal" + fi + + if [[ $2 == "h2" || $2 == "" ]] + then + #wxi-repeat " " $(($(tput cols)/2-${#1}/2)) + echo -n "$wxiBold" + echo ">> $1 <<" + echo -n "$wxiNormal" + fi + + if [[ $2 == "h3" ]] + then + #wxi-repeat " " $(($(tput cols)/2-${#1}/2)) + echo -n "$wxiBold" + echo "$1" + echo -n "$wxiNormal" + fi + + if [[ $2 == "h4" ]] + then + #wxi-repeat " " $(($(tput cols)/2-${#1}/2)) + echo -n "$wxiUnderline" + echo "$1" + echo -n "$wxiNormal" + fi +} + + +wxi-repeat() { + if [[ $1 == " " ]] + then + local str=$1 n=$2 spaces + printf -v spaces "%*s" $n " " + printf "%s" "${spaces// /$str}" + else + for i in $(seq 1 $2); + do + echo -en $1 + done + fi +} + +wx-login(){ + wxi-header "Login" + wxi-restricted --user + wxi-restricted --org + wxi-restricted --vault + + wxi-header "$ORG_HEADER" h3 + + if [[ ! -z ${args['auth-method']} ]] + then + AUTH_METHOD=${args['auth-method']} + elif [[ ! -z ${args['token']} ]] + then + AUTH_METHOD=token + elif [[ $(wxi-config-legacy login read) ]] + then + AUTH_METHOD=token + elif [[ ! -z ${args['username']} ]] + then + AUTH_METHOD=ldap + else + AUTH_METHOD=ldap + fi + + if [[ ! -z $AUTH_METHOD ]] + then + case $AUTH_METHOD in + ldap) + echo -n "Username: " + if [[ ! -z ${args['username']} ]] + then + USERNAME=${args['username']} + wxi-content text "$USERNAME" + else + read USERNAME + fi + + echo -n "Password: " + if [[ ! -z ${args['password']} ]] + then + PASSWORD=${args['password']} + else + read -s PASSWORD + fi + + if [[ ! -z $PASSWORD ]] + then + wxi-content text "****************" + else + wxi-content text "" + fi + + if [[ -z $USERNAME || -z $PASSWORD ]] + then + wxi-content status "Username & Password" "Required" + wxi-stop + fi + + VAULT_LOGIN=$(curl https://$VAULT_DOMAIN/v1/auth/ldap/login/$USERNAME -X POST -d '{ "password": "'$PASSWORD'" }' -s | jq -r '.auth.client_token') + if [[ -z $VAULT_LOGIN || ${#VAULT_LOGIN} -lt 95 || ${#VAULT_LOGIN} -gt 95 ]] + then + wxi-content status "Login" "Failed" + wxi-stop + fi + + TOKEN=$VAULT_LOGIN + wxi-config-legacy login write + ;; + token) + echo -n "Token: " + if [[ ! -z ${args['token']} ]] + then + if [[ ${args['token']} != "true" ]] + then + TOKEN=${args['token']} + fi + elif [[ $(wxi-config-legacy login read) ]] + then + TOKEN=$(wxi-config-legacy login read) + else + read -s TOKEN + fi + + if [[ ! -z $TOKEN ]] + then + wxi-content text "***********************************************************************************************" + fi + + if [[ -z $TOKEN ]] + then + wxi-content status "Token" "Required" + wxi-stop + fi + + if [[ ${#TOKEN} -lt 95 || ${#TOKEN} -gt 95 ]] + then + wxi-content status "Token" "Invalid" + wxi-stop + fi + + VAULT_LOGIN=$(curl https://$VAULT_DOMAIN/v1/auth/token/renew-self -X POST --header "X-Vault-Token: $TOKEN" -d '{ "token": "'$TOKEN'" }' -s | jq -r '.auth.client_token') + if [[ -z $VAULT_LOGIN || ${#VAULT_LOGIN} -lt 95 || ${#VAULT_LOGIN} -gt 95 ]] + then + wxi-content status "Login" "Failed" + + if [[ $(wxi-config-legacy login read) ]] + then + wx-logout &> /dev/null + wx-login + else + wxi-stop + fi + fi + + TOKEN=$VAULT_LOGIN + wxi-config-legacy login write + ;; + *) + wxi-content status "Login Type" "Unsupported" + wxi-stop + ;; + esac + fi + + VAULT_USERNAME=$(curl https://$VAULT_DOMAIN/v1/auth/token/lookup-self -X GET --header "X-Vault-Token: $TOKEN" -s | jq -r '.data.display_name') + if [[ -z $VAULT_USERNAME ]] + then + wxi-content status "Login" "Username Missing" + wxi-stop + elif [[ $VAULT_USERNAME != ldap* && $VAULT_USERNAME != oidc* ]] + then + wxi-content status "Login" "Authentication Method Invalid" + wxi-stop + elif [[ $VAULT_USERNAME == ldap* ]] + then + USERNAME=${VAULT_USERNAME#ldap-} + elif [[ $VAULT_USERNAME == oidc* ]] + then + USERNAME=${VAULT_USERNAME#oidc-} + fi +} + + +wxi-args "$@" +wxi-config + +echo -n $wxiBold$wxiRed +cat <<'EOF' + + (`\ .-') /` ('-. _ .-') ('-. .-') _ + `.( OO ),' ( OO ).-.( \( -O ) _( OO) ( OO ) ) +,--./ .--. / . --. / ,------. (,------.,--./ ,--,' +| | | | \-. \ | /`. ' | .---'| \ | |\ +| | | |,.-'-' | | | / | | | | | \| | ) +| |.'.| |_)\| |_.' | | |_.' |(| '--. | . |/ +| | | .-. | | . '.' | .--' | |\ | +| ,'. | | | | | | |\ \ | `---.| | \ | +'--' '--' `--' `--' `--' '--' `------'`--' `--' + +EOF +echo -n $wxiNormal + +wxi-start +wxi-init +wxi-stop +