Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions container-creation/create-container.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/bin/bash
# Script to create the pct container, run register container, and migrate container accordingly.
# Last Modified by June 30th, 2025 by Maxwell Klema

trap cleanup SIGINT SIGTERM SIGHUP

CONTAINER_NAME="$1"
CONTAINER_PASSWORD="$2"
HTTP_PORT="$3"
PROXMOX_USERNAME="$4"
PUB_FILE="$5"
PROTOCOL_FILE="$6"
NEXT_ID=$(pvesh get /cluster/nextid) #Get the next available LXC ID

# Run cleanup commands in case script is interrupted

function cleanup()
{
BOLD='\033[1m'
RESET='\033[0m'

echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo "⚠️ Script was abruptly exited. Running cleanup tasks."
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
pct unlock 114
if [ -f "/var/lib/vz/snippets/container-public-keys/$PUB_FILE" ]; then
rm -rf /var/lib/vz/snippets/container-public-keys/$PUB_FILE
fi
if [ -f "/var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE" ]; then
rm -rf /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE
fi
exit 1
}


# Create the Container Clone

echo "⏳ Cloning Container..."
pct clone 114 $NEXT_ID \
--hostname $CONTAINER_NAME \
--full true \

# Set Container Options

echo "⏳ Setting Container Properties.."
pct set $NEXT_ID \
--tags "$PROXMOX_USERNAME" \
--onboot 1 \

pct start $NEXT_ID
pveum aclmod /vms/$NEXT_ID --user "$PROXMOX_USERNAME@pve" --role PVEVMUser
#pct delete $NEXT_ID

# Get the Container IP Address and install some packages

echo "⏳ Waiting for DHCP to allocate IP address to container..."
sleep 10

CONTAINER_IP=$(pct exec $NEXT_ID -- hostname -I | awk '{print $1}')
pct exec $NEXT_ID -- apt-get upgrade
pct exec $NEXT_ID -- apt install -y sudo
pct exec $NEXT_ID -- apt install -y git
if [ -f "/var/lib/vz/snippets/container-public-keys/$PUB_FILE" ]; then
pct exec $NEXT_ID -- touch ~/.ssh/authorized_keys
pct exec $NEXT_ID -- bash -c "cat > ~/.ssh/authorized_keys"< /var/lib/vz/snippets/container-public-keys/$PUB_FILE
rm -rf /var/lib/vz/snippets/container-public-keys/$PUB_FILE
fi

# Set password inside the container

pct exec $NEXT_ID -- bash -c "echo 'root:$CONTAINER_PASSWORD' | chpasswd"

# Run Contianer Provision Script to add container to port_map.json

if [ -f "/var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE" ]; then
echo "CONTAINS PROTOCOL FILE"
/var/lib/vz/snippets/register-container-test.sh $NEXT_ID $HTTP_PORT /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE
rm -rf /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE
else
/var/lib/vz/snippets/register-container-test.sh $NEXT_ID $HTTP_PORT
fi

SSH_PORT=$(iptables -t nat -S PREROUTING | grep "to-destination $CONTAINER_IP:22" | awk -F'--dport ' '{print $2}' | awk '{print $1}' | head -n 1 || true)

# Migrate to pve2 if Container ID is even

if (( $NEXT_ID % 2 == 0 )); then
pct stop $NEXT_ID
pct migrate $NEXT_ID intern-phxdc-pve2 --target-storage containers-pve2 --online
ssh root@10.15.0.5 "pct start $NEXT_ID"
fi

# Echo Container Details

# Define friendly, high-contrast colors
BOLD='\033[1m'
BLUE='\033[34m'
MAGENTA='\033[35m'
GREEN='\033[32m'
RESET='\033[0m'

echo -e "📦 ${BLUE}Container ID :${RESET} $NEXT_ID"
echo -e "🌐 ${MAGENTA}Internal IP :${RESET} $CONTAINER_IP"
echo -e "🔗 ${GREEN}Domain Name :${RESET} https://$CONTAINER_NAME.opensource.mieweb.org"
echo -e "🛠️ ${BLUE}SSH Access :${RESET} ssh -p $SSH_PORT root@$CONTAINER_NAME.opensource.mieweb.org"
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
218 changes: 218 additions & 0 deletions container-creation/get-lxc-container-details.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#!/bin/bash
# Main Container Creation Script
# Modified June 23rd, 2025 by Maxwell Klema
# ------------------------------------------

# Define color variables (works on both light and dark backgrounds)
RESET="\033[0m"
BOLD="\033[1m"
MAGENTA='\033[35m'

echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BOLD}${MAGENTA}📦 MIE Container Creation Script ${RESET}"
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"

# Authenticate User (Only Valid Users can Create Containers)

if [ -z "$PROXMOX_USERNAME" ]; then
read -p "Enter Proxmox Username → " PROXMOX_USERNAME
fi

if [ -z "$PROXMOX_PASSWORD" ]; then
read -sp "Enter Proxmox Password → " PROXMOX_PASSWORD
echo ""
fi

USER_AUTHENTICATED=$(node /root/bin/js/authenticateUserRunner.js authenticateUser "$PROXMOX_USERNAME" "$PROXMOX_PASSWORD")
RETRIES=3

while [ $USER_AUTHENTICATED == 'false' ]; do
if [ $RETRIES -gt 0 ]; then
echo "❌ Authentication Failed. Try Again"
read -p "Enter Proxmox Username → " PROXMOX_USERNAME
read -sp "Enter Proxmox Password → " PROXMOX_PASSWORD
echo ""

USER_AUTHENTICATED=$(node /root/bin/js/authenticateUserRunner.js authenticateUser "$PROXMOX_USERNAME" "$PROXMOX_PASSWORD")
RETRIES=$(($RETRIES-1))
else
echo "Too many incorrect attempts. Exiting..."
exit 0
fi
done

echo "🎉 Your proxmox account, $PROXMOX_USERNAME@pve, has been authenticated"

# Gather Container Hostname (hostname.opensource.mieweb.org)

if [ -z "$CONTAINER_NAME" ]; then
read -p "Enter Application Name (One-Word) → " CONTAINER_NAME
fi

HOST_NAME_EXISTS=$(ssh root@10.15.20.69 "node /etc/nginx/checkHostnameRunner.js checkHostnameExists ${CONTAINER_NAME}")

while [ $HOST_NAME_EXISTS == 'true' ]; do
echo "Sorry! That name has already been registered. Try another name"
read -p "Enter Application Name (One-Word) → " CONTAINER_NAME
HOST_NAME_EXISTS=$(ssh root@10.15.20.69 "node /etc/nginx/checkHostnameRunner.js checkHostnameExists ${CONTAINER_NAME}")
done

echo "✅ $CONTAINER_NAME is available"

# Gather Container Password

if [ -z "$CONTAINER_PASSWORD" ]; then
read -sp "Enter Container Password → " CONTAINER_PASSWORD
echo
read -sp "Confirm Container Password → " CONFIRM_PASSWORD
echo

while [[ "$CONFIRM_PASSWORD" != "$CONTAINER_PASSWORD" || ${#CONTAINER_PASSWORD} -lt 8 ]]; do
echo "Sorry, try again. Ensure passwords are at least 8 characters."
read -sp "Enter Container Password → " CONTAINER_PASSWORD
echo
read -sp "Confirm Container Password → " CONFIRM_PASSWORD
echo
done
else
while [ ${#CONTAINER_PASSWORD} -lt 8 ]; do
echo "Sorry, try again. Ensure passwords are at least 8 characters."
read -sp "Enter Container Password → " CONTAINER_PASSWORD
echo
read -sp "Confirm Container Password → " CONFIRM_PASSWORD
echo
done
fi

# Attempt to detect public keys

echo -e "\n🔑 Attempting to Detect SSH Public Key..."

AUTHORIZED_KEYS="/root/.ssh/authorized_keys"
RANDOM_NUM=$(shuf -i 100000-999999 -n 1)
PUB_FILE="key_$RANDOM_NUM.pub"
TEMP_PUB_FILE="/root/bin/ssh/temp_pubs/$PUB_FILE" # in case two users are running this script at the same time, they do not overwrite each other's temp files
touch "$TEMP_PUB_FILE"
DETECT_PUBLIC_KEY=$(sudo /root/bin/ssh/detectPublicKey.sh "$SSH_KEY_FP" "$TEMP_PUB_FILE")

if [ "$DETECT_PUBLIC_KEY" == "Public key found for create-container" ]; then
echo "🔐 Public Key Found!"
else
echo "🔍 Could not detect Public Key"

if [ -z "$PUBLIC_KEY" ]; then
read -p "Enter Public Key (Allows Easy Access to Container) [OPTIONAL - LEAVE BLANK TO SKIP] → " PUBLIC_KEY
fi

# Check if key is valid

while [[ "$PUBLIC_KEY" != "" && $(echo "$PUBLIC_KEY" | ssh-keygen -l -f - 2>&1 | tr -d '\r') == "(stdin) is not a public key file." ]]; do
echo "❌ \"$PUBLIC_KEY\" is not a valid key. Enter either a valid key or leave blank to skip."
read -p "Enter Public Key (Allows Easy Access to Container) [OPTIONAL - LEAVE BLANK TO SKIP] → " PUBLIC_KEY
done

if [ "$PUBLIC_KEY" != "" ]; then
echo "$PUBLIC_KEY" > "$AUTHORIZED_KEYS" && systemctl restart ssh
echo "$PUBLIC_KEY" > "$TEMP_PUB_FILE"
sudo /root/bin/ssh/publicKeyAppendJumpHost.sh "$PUBLIC_KEY"
fi
fi

# Get HTTP Port Container Listens On

if [ -z "$HTTP_PORT" ]; then
read -p "Enter HTTP Port for your container to listen on (80-9999) → " HTTP_PORT
fi

while ! [[ "$HTTP_PORT" =~ ^[0-9]+$ ]] || [ "$HTTP_PORT" -lt 80 ] || [ "$HTTP_PORT" -gt 9999 ]; do
echo "❌ Invalid HTTP Port. It must be a number between 80 and 9,999."
read -p "Enter HTTP Port for your container to listen on (80-9999) → " HTTP_PORT
done

echo "✅ HTTP Port is set to $HTTP_PORT"

# Get any other protocols

protocol_duplicate() {
PROTOCOL="$1"
shift #remaining params are part of list
LIST="$@"

for item in $LIST; do
if [[ "$item" == "$PROTOCOL" ]]; then
return 0 # Protocol is a duplicate
fi
done
return 1 # Protocol is not a duplicate
}

read -p "Does your Container require any protocols other than SSH and HTTP? (y/n) → " USE_OTHER_PROTOCOLS
while [ "${USE_OTHER_PROTOCOLS^^}" != "Y" ] && [ "${USE_OTHER_PROTOCOLS^^}" != "N" ]; do
echo "Please answer 'y' for yes or 'n' for no."
read -p "Does your Container require any protocols other than SSH and HTTP? (y/n) → " USE_OTHER_PROTOCOLS
done

RANDOM_NUM=$(shuf -i 100000-999999 -n 1)
PROTOCOL_BASE_FILE="protocol_list_$RANDOM_NUM.txt"
PROTOCOL_FILE="/root/bin/protocols/$PROTOCOL_BASE_FILE"
touch "$PROTOCOL_FILE"

if [ "${USE_OTHER_PROTOCOLS^^}" == "Y" ]; then
LIST_PROTOCOLS=()
read -p "Enter the protocol abbreviation (e.g, LDAP for Lightweight Directory Access Protocol). Type \"e\" to exit → " PROTOCOL_NAME
while [ "${PROTOCOL_NAME^^}" != "E" ]; do
FOUND=0 #keep track if protocol was found
while read line; do
PROTOCOL_ABBRV=$(echo "$line" | awk '{print $1}')
protocol_duplicate "$PROTOCOL_ABBRV" "${LIST_PROTOCOLS[@]}"
IS_PROTOCOL_DUPLICATE=$?
if [[ "$PROTOCOL_ABBRV" == "${PROTOCOL_NAME^^}" && "$IS_PROTOCOL_DUPLICATE" -eq 1 ]]; then
LIST_PROTOCOLS+=("$PROTOCOL_ABBRV")
PROTOCOL_UNDRLYING_NAME=$(echo "$line" | awk '{print $3}')
PROTOCOL_DEFAULT_PORT=$(echo "$line" | awk '{print $2}')
echo "$PROTOCOL_ABBRV $PROTOCOL_UNDRLYING_NAME $PROTOCOL_DEFAULT_PORT" >> "$PROTOCOL_FILE"
echo "✅ Protocol ${PROTOCOL_NAME^^} added to container."
FOUND=1 #protocol was found
break
else
echo "❌ Protocol ${PROTOCOL_NAME^^} was already added to your container. Please try again."
FOUND=2 #protocol was a duplicate
break
fi
done < <(cat "/root/bin/protocols/master_protocol_list.txt" | grep "^${PROTOCOL_NAME^^}")

if [ $FOUND -eq 0 ]; then #if no results found, let user know.
echo "❌ Protocol ${PROTOCOL_NAME^^} not found. Please try again."
fi

read -p "Enter the protocol abbreviation (e.g, LDAP for Lightweight Directory Access Protocol). Type \"e\" to exit → " PROTOCOL_NAME
done
fi

# send public key file & port map file to hypervisor and ssh, Create the Container, run port mapping script

if [ -s $TEMP_PUB_FILE ]; then
sftp root@10.15.0.4 <<EOF
put $TEMP_PUB_FILE /var/lib/vz/snippets/container-public-keys/
EOF
fi

# don't send it file size is zero.
if [ -s "$PROTOCOL_FILE" ]; then
sftp root@10.15.0.4 <<EOF
put $PROTOCOL_FILE /var/lib/vz/snippets/container-port-maps/
EOF
fi

echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BOLD}${MAGENTA}🚀 Starting Container Creation...${RESET}"
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"

ssh -t root@10.15.0.4 "/var/lib/vz/snippets/create-container.sh $CONTAINER_NAME $CONTAINER_PASSWORD $HTTP_PORT $PROXMOX_USERNAME $PUB_FILE $PROTOCOL_BASE_FILE"

rm -rf "$PROTOCOL_FILE"
rm -rf "$TEMP_PUB_FILE"

unset CONFIRM_PASSWORD
unset CONTAINER_PASSWORD
unset PUBLIC_KEY
30 changes: 30 additions & 0 deletions container-creation/js/authenticateUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Script to authenticate a user into Proxmox
// Last updated June 24th, 2025 by Maxwell Klema

const axios = require('axios');
const qs = require('qs');
const https = require('https');

// authenticates user, ensuring they have a valid proxmox account
function authenticateUser(username, password) {
let data = qs.stringify({
'username': username + "@pve",
'password': password
})

let config = {
method: 'post',
url: ' https://10.15.0.4:8006/api2/json/access/ticket',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
httpsAgent: new https.Agent({
rejectUnauthorized: false // Disable SSL verification for self-signed certificates (Only because public facing domain is resolved to nginx server internally, so have to use hypervisor IP instead of domain)
}),
data: data
};

return axios.request(config).then((response) => response.status === 200).catch(() => false);
}

module.exports = { authenticateUser };
11 changes: 11 additions & 0 deletions container-creation/js/authenticateUserRunner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Script to run authenticateUser in the shell
// Last updated June 24th, 2025 by Maxwell Klema

authenticateuser = require("./authenticateUser.js");

const [, , func, ...args] = process.argv;
if (func == "authenticateUser") {
authenticateuser.authenticateUser(...args).then((result) => {
console.log(result);
});
}
25 changes: 25 additions & 0 deletions container-creation/ssh/detectPublicKey.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
# Detect if the user in the current session logged in via an SSH public key
# Last Updated June 26, 2025 Maxwell Klema

USER="create-container" #Change Later
PUBLIC_KEY_LIST="/root/.ssh/authorized_keys"


KEY_FINGERPRINT="$1"
TEMP_PUB_FILE="$2"

if [ "$KEY_FINGERPRINT" != "" ]; then
# Iterate over each public key, compute fingerprint, see if there is a match

while read line; do
echo "$line" > "$TEMP_PUB_FILE"
PUB_FINGERPRINT=$(ssh-keygen -lf "$TEMP_PUB_FILE" | awk '{print $2}')
if [[ "$PUB_FINGERPRINT" == "$KEY_FINGERPRINT" ]]; then
echo "Public key found for $USER"
exit 0
fi
done < <(tac $PUBLIC_KEY_LIST) #Iterates backwards without creating subprocess (allows exit in loop)

echo "" > "$TEMP_PUB_FILE"
fi
Loading