Compare commits
36
Commits
aa9bac2c5b
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d40a3d4239 | ||
|
|
0d21e43a8f | ||
|
|
b582bb9574 | ||
|
|
0809224963 | ||
|
|
9ae5c8bbd0 | ||
|
|
370a46f9cf | ||
|
|
50c56ad5f7 | ||
|
|
bb0514469d | ||
|
|
b3db6f0f82 | ||
|
|
7bc8fb8f06 | ||
|
|
443c5a03c1 | ||
|
|
9ee3b5b40f | ||
|
|
3dec31c52e | ||
|
|
1d48721308 | ||
|
|
2c7bd4ac76 | ||
|
|
402d997599 | ||
|
|
12e8546023 | ||
|
|
46d79166a3 | ||
|
|
3f27ac8960 | ||
|
|
337ef1e54c | ||
|
|
3ba8f9d1d4 | ||
|
|
5de21476ad | ||
|
|
dc0c37c40d | ||
|
|
12fe681efa | ||
|
|
6e47dfb7b6 | ||
|
|
c4044139d1 | ||
|
|
d91a7e9426 | ||
|
|
b49fcb3358 | ||
|
|
96ff742bd5 | ||
|
|
c68fadd9aa | ||
|
|
a707af5b7a | ||
|
|
9739367f38 | ||
|
|
4895b7d733 | ||
|
|
d27a6dd76f | ||
|
|
c19ec14cfd | ||
|
|
4e1129c368 |
@@ -42,3 +42,35 @@ The installer will:
|
||||
- Generate a secure LUKS passphrase (SAVE IT!)
|
||||
- Download and configure everything
|
||||
- Run Hetzner's installimage automatically
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<img src='./icon_cluster.svg' width="150px">
|
||||
<h2>nullpoint cluster</h2>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
Encrypted network and storage pool using [Nebula](https://github.com/slackhq/nebula) mesh VPN and [GlusterFS](https://www.gluster.org/) distributed filesystem.
|
||||
|
||||
## Features
|
||||
|
||||
- **Encrypted mesh network** - All traffic encrypted via Nebula overlay (192.168.100.0/24)
|
||||
- **Distributed storage** - Data replicated across all storage nodes
|
||||
- **Simple joining** - Single preshared secret + lighthouse endpoint
|
||||
- **Flexible nodes** - Full nodes (replicate data) or remote nodes (no storage)
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
wget -qO- https://git.dominik-roth.eu/dodox/nullpoint/raw/branch/master/cluster-setup.sh | sudo bash
|
||||
```
|
||||
|
||||
Choose your node type:
|
||||
- **Full node** - Runs GlusterFS server, contributes storage, acts as lighthouse
|
||||
- Use for servers in same datacenter/region with low latency
|
||||
- **Remote node** - GlusterFS client only, no storage contribution
|
||||
- Use for edge locations, different regions, or high-latency connections
|
||||
- Avoids replication delays since writes don't wait for this node
|
||||
|
||||
Storage mounted at `/data/storage/` on all nodes.
|
||||
|
||||
Executable
+564
@@ -0,0 +1,564 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
NEBULA_NETWORK="192.168.100.0/24"
|
||||
NEBULA_PORT=4242
|
||||
NEBULA_CONFIG="/etc/nebula"
|
||||
GLUSTER_BRICK_PATH="/gluster/cluster"
|
||||
GLUSTER_MOUNT_PATH="/data/storage"
|
||||
GLUSTER_VOLUME="cluster-volume"
|
||||
NEBULA_VERSION="v1.9.6"
|
||||
|
||||
CLUSTER_LOGO=$(cat << "EOF"
|
||||
==
|
||||
.@@@@@
|
||||
=@@@@@%
|
||||
+@@@@@
|
||||
:@@@@@
|
||||
*@@@@: -*#+:
|
||||
*@@@- %@@@@ .%@@@@@@@@@@@@*
|
||||
@@@@@ %@@@@ *@@@@@@@@@@@@@@@@
|
||||
+@@@@* #@@@@. %@@@@@@@.
|
||||
@@@@@@ -@@@@%:@@@@@@:
|
||||
*@@@@@@. #@@@@@@@@@:
|
||||
.@@@@@@@@%=.#@@@@@@@@@@@@@#.
|
||||
*@@@@@@@@@@@@@@@@@@@@@@@@@@+
|
||||
=@@@@@@@@@@@@@@%=#@@@@@@@@%
|
||||
:@@@@@@@@@+ -@@@@@@+
|
||||
*@@@@@-%@@@@: .@@@@@#
|
||||
=@@@@@@@ .@@@@# #@@@@+
|
||||
%@@@@@@@@@@@@@@@@. @@@@@ @@@@@
|
||||
#@@@@@@@@@@@@@= @@@@@ =@@@%
|
||||
:#@@@@- .@@@@#
|
||||
#@@@@-
|
||||
#@@@@%
|
||||
+@@@@@*
|
||||
@@@@@-
|
||||
.#%.
|
||||
|
||||
[nullpoint cluster]
|
||||
EOF
|
||||
)
|
||||
|
||||
echo -e "${GREEN}${CLUSTER_LOGO}${NC}\n"
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "${RED}Please run as root${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install base packages
|
||||
echo -e "${YELLOW}[+] Installing base packages...${NC}"
|
||||
dnf install -y curl tar || exit 1
|
||||
|
||||
# Download and install Nebula
|
||||
echo -e "${YELLOW}[+] Downloading Nebula ${NEBULA_VERSION}...${NC}"
|
||||
cd /tmp
|
||||
curl -LO "https://github.com/slackhq/nebula/releases/download/${NEBULA_VERSION}/nebula-linux-amd64.tar.gz"
|
||||
tar -zxf nebula-linux-amd64.tar.gz
|
||||
mv nebula nebula-cert /usr/local/bin/
|
||||
chmod +x /usr/local/bin/nebula /usr/local/bin/nebula-cert
|
||||
|
||||
# Create directories
|
||||
echo -e "${YELLOW}[+] Creating directories...${NC}"
|
||||
mkdir -p "$GLUSTER_MOUNT_PATH"
|
||||
mkdir -p /data
|
||||
mkdir -p "$NEBULA_CONFIG"
|
||||
|
||||
# Function to generate Nebula CA and certificates
|
||||
generate_nebula_ca() {
|
||||
echo -e "${YELLOW}[+] Generating Nebula CA...${NC}"
|
||||
cd "$NEBULA_CONFIG"
|
||||
/usr/local/bin/nebula-cert ca -name "Nullpoint Cluster CA"
|
||||
chmod 600 ca.key
|
||||
}
|
||||
|
||||
# Function to create host certificate
|
||||
create_host_cert() {
|
||||
local hostname="$1"
|
||||
local ip="$2"
|
||||
local groups="$3"
|
||||
|
||||
cd "$NEBULA_CONFIG"
|
||||
/usr/local/bin/nebula-cert sign -name "$hostname" -ip "$ip" -groups "$groups"
|
||||
chmod 600 "${hostname}.key"
|
||||
}
|
||||
|
||||
# Function to get next available IP
|
||||
get_next_ip() {
|
||||
local base_ip="192.168.100"
|
||||
local next_num=10
|
||||
|
||||
if [ -f "$NEBULA_CONFIG/cluster-registry.txt" ]; then
|
||||
# Find highest IP in use
|
||||
existing_ips=$(grep -oE "192\.168\.100\.[0-9]+" "$NEBULA_CONFIG/cluster-registry.txt" | cut -d. -f4 | sort -n | tail -1)
|
||||
if [ ! -z "$existing_ips" ]; then
|
||||
next_num=$((existing_ips + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "${base_ip}.${next_num}"
|
||||
}
|
||||
|
||||
# Function to setup firewall rules
|
||||
setup_firewall() {
|
||||
echo -e "${YELLOW}[+] Configuring firewall...${NC}"
|
||||
|
||||
# Nebula
|
||||
firewall-cmd --permanent --add-port=${NEBULA_PORT}/udp
|
||||
|
||||
# GlusterFS ports
|
||||
firewall-cmd --permanent --add-service=glusterfs
|
||||
firewall-cmd --permanent --add-port=24007-24008/tcp # GlusterFS Daemon
|
||||
firewall-cmd --permanent --add-port=49152-49200/tcp # Brick ports
|
||||
|
||||
# Allow traffic from Nebula network
|
||||
firewall-cmd --permanent --zone=trusted --add-source=${NEBULA_NETWORK}
|
||||
|
||||
firewall-cmd --reload
|
||||
}
|
||||
|
||||
# Create Nebula systemd service
|
||||
create_nebula_service() {
|
||||
cat > /etc/systemd/system/nebula.service <<EOF
|
||||
[Unit]
|
||||
Description=Nebula overlay networking tool
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
ExecStart=/usr/local/bin/nebula -config ${NEBULA_CONFIG}/config.yaml
|
||||
ExecReload=/bin/kill -HUP \$MAINPID
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
}
|
||||
|
||||
# Create new cluster
|
||||
create_cluster() {
|
||||
echo -e "${GREEN}[*] Creating new cluster...${NC}\n"
|
||||
|
||||
local hostname=$(hostname)
|
||||
local node_ip="192.168.100.1"
|
||||
|
||||
# First cluster node must be full node
|
||||
echo -e "${YELLOW}First cluster node must be a full node (storage provider)${NC}"
|
||||
|
||||
# Install GlusterFS server packages
|
||||
echo -e "${YELLOW}[+] Installing GlusterFS server packages...${NC}"
|
||||
dnf install -y glusterfs-server || exit 1
|
||||
systemctl enable glusterd
|
||||
systemctl start glusterd
|
||||
|
||||
# Create brick directory
|
||||
mkdir -p "$GLUSTER_BRICK_PATH"
|
||||
|
||||
# Ask for lighthouse endpoints
|
||||
echo -e "${YELLOW}Enter lighthouse endpoints (DNS names or IPs).${NC}"
|
||||
echo -e "${YELLOW}Recommended: Use a DNS name with redundant backing for HA.${NC}"
|
||||
echo -e "${YELLOW}You can enter multiple endpoints separated by commas.${NC}"
|
||||
read -p "Lighthouse endpoints (e.g., cluster.example.com or 1.2.3.4,5.6.7.8): " lighthouse_endpoints
|
||||
if [ -z "$lighthouse_endpoints" ]; then
|
||||
echo -e "${RED}At least one lighthouse endpoint required!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
am_lighthouse="true"
|
||||
|
||||
# Generate Nebula CA
|
||||
generate_nebula_ca
|
||||
|
||||
# Create certificate for this node
|
||||
create_host_cert "$hostname" "${node_ip}/24" "cluster"
|
||||
|
||||
# Create Nebula config
|
||||
cat > "${NEBULA_CONFIG}/config.yaml" <<EOF
|
||||
pki:
|
||||
ca: ${NEBULA_CONFIG}/ca.crt
|
||||
cert: ${NEBULA_CONFIG}/${hostname}.crt
|
||||
key: ${NEBULA_CONFIG}/${hostname}.key
|
||||
|
||||
lighthouse:
|
||||
am_lighthouse: ${am_lighthouse}
|
||||
serve_dns: false
|
||||
interval: 60
|
||||
hosts:$(echo "$lighthouse_endpoints" | tr ',' '\n' | while read endpoint; do echo "
|
||||
- \"${endpoint}:${NEBULA_PORT}\""; done)
|
||||
|
||||
listen:
|
||||
host: 0.0.0.0
|
||||
port: ${NEBULA_PORT}
|
||||
|
||||
punchy:
|
||||
punch: true
|
||||
respond: true
|
||||
|
||||
tun:
|
||||
disabled: false
|
||||
dev: nebula1
|
||||
drop_local_broadcast: false
|
||||
drop_multicast: false
|
||||
tx_queue: 500
|
||||
mtu: 1300
|
||||
|
||||
logging:
|
||||
level: info
|
||||
format: text
|
||||
|
||||
firewall:
|
||||
conntrack:
|
||||
tcp_timeout: 12m
|
||||
udp_timeout: 3m
|
||||
default_timeout: 10m
|
||||
max_connections: 100000
|
||||
|
||||
outbound:
|
||||
- port: any
|
||||
proto: any
|
||||
host: any
|
||||
|
||||
inbound:
|
||||
- port: any
|
||||
proto: icmp
|
||||
host: any
|
||||
- port: any
|
||||
proto: any
|
||||
host: any
|
||||
EOF
|
||||
|
||||
# Start Nebula as systemd service
|
||||
create_nebula_service
|
||||
systemctl enable nebula
|
||||
systemctl start nebula
|
||||
|
||||
# Setup firewall
|
||||
setup_firewall
|
||||
|
||||
# Create cluster registry
|
||||
echo "${node_ip} ${hostname} full $(date)" > "${NEBULA_CONFIG}/cluster-registry.txt"
|
||||
|
||||
# Create GlusterFS volume
|
||||
echo -e "${YELLOW}[+] Creating GlusterFS volume...${NC}"
|
||||
mkdir -p "${GLUSTER_BRICK_PATH}/brick1"
|
||||
gluster volume create ${GLUSTER_VOLUME} $(hostname):${GLUSTER_BRICK_PATH}/brick1 force 2>/dev/null || true
|
||||
gluster volume start ${GLUSTER_VOLUME} 2>/dev/null || true
|
||||
|
||||
# Mount volume
|
||||
mount -t glusterfs localhost:/${GLUSTER_VOLUME} ${GLUSTER_MOUNT_PATH}
|
||||
grep -q "${GLUSTER_VOLUME}" /etc/fstab || echo "localhost:/${GLUSTER_VOLUME} ${GLUSTER_MOUNT_PATH} glusterfs defaults,_netdev 0 0" >> /etc/fstab
|
||||
|
||||
# Package CA certificate for sharing
|
||||
ca_cert_b64=$(base64 -w0 "${NEBULA_CONFIG}/ca.crt")
|
||||
|
||||
echo -e "\n${GREEN}════════════════════════════════════════${NC}"
|
||||
echo -e "${GREEN}Cluster created successfully!${NC}"
|
||||
echo -e "${GREEN}════════════════════════════════════════${NC}\n"
|
||||
echo -e "Share this cluster secret with joining nodes:\n"
|
||||
echo -e "${GREEN}${lighthouse_endpoints}:${NEBULA_PORT}:${ca_cert_b64}${NC}\n"
|
||||
echo -e "${YELLOW}Status:${NC}"
|
||||
echo " - Nebula IP: ${node_ip}"
|
||||
echo " - Lighthouse endpoints: ${lighthouse_endpoints}:${NEBULA_PORT}"
|
||||
echo " - This node is lighthouse: ${am_lighthouse}"
|
||||
echo " - GlusterFS volume: ${GLUSTER_VOLUME}"
|
||||
echo " - Mount point: ${GLUSTER_MOUNT_PATH}"
|
||||
}
|
||||
|
||||
# Join existing cluster
|
||||
join_cluster() {
|
||||
echo -e "${GREEN}[*] Joining existing cluster...${NC}\n"
|
||||
|
||||
local hostname=$(hostname)
|
||||
local my_ip=$(get_next_ip)
|
||||
|
||||
# Get cluster details
|
||||
read -p "Enter cluster secret (lighthouse_endpoints:port:ca_cert_base64): " cluster_secret
|
||||
|
||||
if [ -z "$cluster_secret" ]; then
|
||||
echo -e "${RED}Cluster secret required!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse secret
|
||||
lighthouse_endpoints=$(echo "$cluster_secret" | cut -d: -f1)
|
||||
nebula_port=$(echo "$cluster_secret" | cut -d: -f2)
|
||||
ca_cert_b64=$(echo "$cluster_secret" | cut -d: -f3-)
|
||||
|
||||
# Ask about node type
|
||||
echo -e "${YELLOW}Select node type:${NC}"
|
||||
echo " 1) Full node (contributes storage, lighthouse, read/write)"
|
||||
echo " 2) Remote node (client only, no storage contribution)"
|
||||
echo -e "${YELLOW}Note: Use remote nodes for locations with high latency to the cluster${NC}"
|
||||
read -p "Enter choice [1-2]: " node_type
|
||||
|
||||
if [ "$node_type" = "2" ]; then
|
||||
is_remote="true"
|
||||
am_lighthouse="false"
|
||||
echo -e "${YELLOW}Configuring as remote node (GlusterFS client only)${NC}"
|
||||
# Install only GlusterFS client packages
|
||||
echo -e "${YELLOW}[+] Installing GlusterFS client packages...${NC}"
|
||||
dnf install -y glusterfs glusterfs-fuse || exit 1
|
||||
else
|
||||
is_remote="false"
|
||||
am_lighthouse="true"
|
||||
echo -e "${YELLOW}Configuring as full node (GlusterFS server)${NC}"
|
||||
# Install GlusterFS server packages
|
||||
echo -e "${YELLOW}[+] Installing GlusterFS server packages...${NC}"
|
||||
dnf install -y glusterfs-server || exit 1
|
||||
systemctl enable glusterd
|
||||
systemctl start glusterd
|
||||
# Create brick directory for full nodes
|
||||
mkdir -p "$GLUSTER_BRICK_PATH"
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}[+] Configuring Nebula (IP: ${my_ip})...${NC}"
|
||||
|
||||
# Decode and save CA certificate
|
||||
echo "$ca_cert_b64" | base64 -d > "${NEBULA_CONFIG}/ca.crt"
|
||||
|
||||
echo -e "${RED}WARNING: Certificate signing not implemented in this simplified version.${NC}"
|
||||
echo -e "${YELLOW}On the lighthouse node, run this command to create a certificate for this node:${NC}"
|
||||
echo -e "${GREEN}cd ${NEBULA_CONFIG} && /usr/local/bin/nebula-cert sign -name \"${hostname}\" -ip \"${my_ip}/24\" -groups \"cluster\"${NC}"
|
||||
echo -e "${YELLOW}Then copy ${hostname}.crt and ${hostname}.key to ${NEBULA_CONFIG}/ on this node.${NC}"
|
||||
|
||||
read -p "Press enter once you've created and copied the certificate files..."
|
||||
|
||||
# Create Nebula config
|
||||
cat > "${NEBULA_CONFIG}/config.yaml" <<EOF
|
||||
pki:
|
||||
ca: ${NEBULA_CONFIG}/ca.crt
|
||||
cert: ${NEBULA_CONFIG}/${hostname}.crt
|
||||
key: ${NEBULA_CONFIG}/${hostname}.key
|
||||
|
||||
lighthouse:
|
||||
am_lighthouse: ${am_lighthouse}
|
||||
interval: 60
|
||||
hosts:$(echo "$lighthouse_endpoints" | tr ',' '\n' | while read endpoint; do echo "
|
||||
- \"${endpoint}:${nebula_port}\""; done)
|
||||
|
||||
listen:
|
||||
host: 0.0.0.0
|
||||
port: ${NEBULA_PORT}
|
||||
|
||||
punchy:
|
||||
punch: true
|
||||
respond: true
|
||||
|
||||
tun:
|
||||
disabled: false
|
||||
dev: nebula1
|
||||
drop_local_broadcast: false
|
||||
drop_multicast: false
|
||||
tx_queue: 500
|
||||
mtu: 1300
|
||||
|
||||
logging:
|
||||
level: info
|
||||
format: text
|
||||
|
||||
firewall:
|
||||
conntrack:
|
||||
tcp_timeout: 12m
|
||||
udp_timeout: 3m
|
||||
default_timeout: 10m
|
||||
max_connections: 100000
|
||||
|
||||
outbound:
|
||||
- port: any
|
||||
proto: any
|
||||
host: any
|
||||
|
||||
inbound:
|
||||
- port: any
|
||||
proto: icmp
|
||||
host: any
|
||||
- port: any
|
||||
proto: any
|
||||
host: any
|
||||
EOF
|
||||
|
||||
# Start Nebula
|
||||
create_nebula_service
|
||||
systemctl enable nebula
|
||||
systemctl start nebula
|
||||
|
||||
# Setup firewall
|
||||
setup_firewall
|
||||
|
||||
# Wait for Nebula connection
|
||||
echo -e "${YELLOW}[+] Waiting for Nebula connection...${NC}"
|
||||
sleep 5
|
||||
|
||||
# Test connection - try pinging the first node
|
||||
echo -e "${YELLOW}[+] Testing Nebula connection...${NC}"
|
||||
if ping -c 1 -W 3 192.168.100.1 > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}[✓] Connected to node at 192.168.100.1${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}[!] Could not reach 192.168.100.1 - this may be normal if it's the first node${NC}"
|
||||
fi
|
||||
|
||||
# Register with cluster
|
||||
node_type_str=$([ "$is_remote" = "true" ] && echo "remote" || echo "full")
|
||||
echo "${my_ip} ${hostname} ${node_type_str} $(date)" >> "${NEBULA_CONFIG}/cluster-registry.txt"
|
||||
|
||||
# Handle GlusterFS setup based on node type
|
||||
if [ "$is_remote" = "true" ]; then
|
||||
# Remote node - GlusterFS client only
|
||||
echo -e "${YELLOW}[+] Mounting GlusterFS as client...${NC}"
|
||||
|
||||
# Find a full node to connect to (try first few IPs)
|
||||
mount_successful=false
|
||||
for ip in 192.168.100.1 192.168.100.2 192.168.100.3; do
|
||||
if ping -c 1 -W 2 $ip > /dev/null 2>&1; then
|
||||
echo -e "${YELLOW}Attempting to mount from $ip...${NC}"
|
||||
if mount -t glusterfs ${ip}:/${GLUSTER_VOLUME} ${GLUSTER_MOUNT_PATH} 2>/dev/null; then
|
||||
mount_successful=true
|
||||
mount_ip=$ip
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$mount_successful" = "true" ]; then
|
||||
# Add to fstab
|
||||
grep -q "${GLUSTER_VOLUME}" /etc/fstab || echo "${mount_ip}:/${GLUSTER_VOLUME} ${GLUSTER_MOUNT_PATH} glusterfs defaults,_netdev,backup-volfile-servers=192.168.100.1:192.168.100.2:192.168.100.3 0 0" >> /etc/fstab
|
||||
echo -e "${GREEN}Remote node configured - mounted cluster storage as client${NC}"
|
||||
else
|
||||
echo -e "${RED}Failed to mount GlusterFS volume!${NC}"
|
||||
echo "Make sure at least one full node is running."
|
||||
fi
|
||||
else
|
||||
# Full node - GlusterFS server
|
||||
echo -e "${YELLOW}[+] Joining GlusterFS cluster as server...${NC}"
|
||||
|
||||
# Try to probe existing nodes
|
||||
echo -e "${YELLOW}[+] Looking for existing GlusterFS peers...${NC}"
|
||||
peer_found=false
|
||||
for ip in 192.168.100.1 192.168.100.2 192.168.100.3; do
|
||||
if [ "$ip" != "${my_ip}" ] && ping -c 1 -W 2 $ip > /dev/null 2>&1; then
|
||||
if gluster peer probe $ip 2>/dev/null; then
|
||||
echo "Connected to peer at $ip"
|
||||
peer_found=true
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$peer_found" = "false" ]; then
|
||||
echo -e "${YELLOW}No existing peers found - this might be normal for early nodes${NC}"
|
||||
fi
|
||||
|
||||
# Wait for peer connection
|
||||
sleep 3
|
||||
|
||||
# Create brick directory
|
||||
mkdir -p "${GLUSTER_BRICK_PATH}/brick1"
|
||||
|
||||
if [ "$peer_found" = "true" ]; then
|
||||
# Add brick to existing volume
|
||||
echo -e "${YELLOW}[+] Adding brick to GlusterFS volume...${NC}"
|
||||
|
||||
# Get current replica count
|
||||
replica_count=$(gluster volume info ${GLUSTER_VOLUME} 2>/dev/null | grep "Number of Bricks" | grep -oE "[0-9]+" | head -1)
|
||||
if [ ! -z "$replica_count" ]; then
|
||||
new_replica_count=$((replica_count + 1))
|
||||
gluster volume add-brick ${GLUSTER_VOLUME} replica ${new_replica_count} $(hostname):${GLUSTER_BRICK_PATH}/brick1 force
|
||||
fi
|
||||
fi
|
||||
|
||||
# Mount the volume locally
|
||||
mount -t glusterfs localhost:/${GLUSTER_VOLUME} ${GLUSTER_MOUNT_PATH} 2>/dev/null ||
|
||||
mount -t glusterfs 192.168.100.1:/${GLUSTER_VOLUME} ${GLUSTER_MOUNT_PATH} 2>/dev/null
|
||||
|
||||
# Add to fstab
|
||||
grep -q "${GLUSTER_VOLUME}" /etc/fstab || echo "localhost:/${GLUSTER_VOLUME} ${GLUSTER_MOUNT_PATH} glusterfs defaults,_netdev 0 0" >> /etc/fstab
|
||||
|
||||
echo -e "${GREEN}Full node configured - contributing storage to cluster${NC}"
|
||||
fi
|
||||
|
||||
echo -e "\n${GREEN}════════════════════════════════════════${NC}"
|
||||
echo -e "${GREEN}Successfully joined cluster!${NC}"
|
||||
echo -e "${GREEN}════════════════════════════════════════${NC}\n"
|
||||
echo -e "${YELLOW}Node details:${NC}"
|
||||
echo " - Nebula IP: ${my_ip}"
|
||||
echo " - Hostname: ${hostname}"
|
||||
echo " - Node type: $([ "$is_remote" = "true" ] && echo "Remote (no storage)" || echo "Full (contributes storage)")"
|
||||
echo " - GlusterFS mounted at: ${GLUSTER_MOUNT_PATH}"
|
||||
}
|
||||
|
||||
# Show cluster status
|
||||
show_status() {
|
||||
echo -e "\n${YELLOW}=== Cluster Status ===${NC}\n"
|
||||
|
||||
if [ -f "${NEBULA_CONFIG}/config.yaml" ]; then
|
||||
echo -e "${GREEN}Nebula Status:${NC}"
|
||||
systemctl is-active nebula && echo "Service: Active" || echo "Service: Inactive"
|
||||
|
||||
if ip addr show nebula1 >/dev/null 2>&1; then
|
||||
echo "Interface: nebula1 $(ip addr show nebula1 | grep 'inet ' | awk '{print $2}')"
|
||||
else
|
||||
echo "Interface: Not found"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [ -f "${NEBULA_CONFIG}/cluster-registry.txt" ]; then
|
||||
echo -e "${GREEN}Cluster Nodes:${NC}"
|
||||
cat "${NEBULA_CONFIG}/cluster-registry.txt"
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}Nebula not configured${NC}\n"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}GlusterFS Status:${NC}"
|
||||
gluster peer status 2>/dev/null || echo "Not connected to cluster"
|
||||
echo ""
|
||||
gluster volume status ${GLUSTER_VOLUME} 2>/dev/null || echo "Volume ${GLUSTER_VOLUME} not found"
|
||||
echo ""
|
||||
|
||||
echo -e "${GREEN}Mounted at:${NC} ${GLUSTER_MOUNT_PATH}"
|
||||
df -h ${GLUSTER_MOUNT_PATH} 2>/dev/null || echo "Not mounted"
|
||||
}
|
||||
|
||||
# Main menu
|
||||
echo "What would you like to do?"
|
||||
echo " 1) Create new cluster"
|
||||
echo " 2) Join existing cluster"
|
||||
echo " 3) Show cluster status"
|
||||
echo " 4) Exit"
|
||||
echo ""
|
||||
read -p "Enter choice [1-4]: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
create_cluster
|
||||
;;
|
||||
2)
|
||||
join_cluster
|
||||
;;
|
||||
3)
|
||||
show_status
|
||||
;;
|
||||
4)
|
||||
echo "Exiting..."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Invalid choice${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
+14
-3
@@ -15,6 +15,9 @@ autoload -U +X bashcompinit && bashcompinit
|
||||
|
||||
export EDITOR='nvim'
|
||||
|
||||
# nullpoint custom fastfetch
|
||||
alias fastfetch='fastfetch --file-raw /etc/nullpoint-logo'
|
||||
|
||||
if [ $TILIX_ID ] || [ $VTE_VERSION ]; then
|
||||
[ -f /etc/profile.d/vte.sh ] && source /etc/profile.d/vte.sh
|
||||
fi
|
||||
@@ -24,11 +27,12 @@ fi
|
||||
##########
|
||||
|
||||
# lang
|
||||
alias python='python3.13'
|
||||
alias pip='python -m pip'
|
||||
alias ptpython="python -m ptpython"
|
||||
alias ptpy="python -m ptpython"
|
||||
alias py13='python3.13'
|
||||
alias py12='python3.12'
|
||||
alias py10='python3.10'
|
||||
alias py9='python3.9'
|
||||
alias bpytop="python -m bpytop"
|
||||
|
||||
# python venv
|
||||
@@ -127,5 +131,12 @@ alias ska="tmux kill-session -a"
|
||||
|
||||
#####
|
||||
|
||||
export PATH=$PATH:$HOME/.local/bin
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
|
||||
# Enable bash completion compatibility in zsh
|
||||
autoload -U +X bashcompinit && bashcompinit
|
||||
autoload -U +X compinit && compinit
|
||||
|
||||
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
|
||||
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
height="200px"
|
||||
width="200px"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
sodipodi:docname="icon_cluster.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs3" />
|
||||
<sodipodi:namedview
|
||||
id="namedview3"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="5.59"
|
||||
inkscape:cx="100"
|
||||
inkscape:cy="100"
|
||||
inkscape:window-width="3440"
|
||||
inkscape:window-height="1371"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3" />
|
||||
<path
|
||||
d="M12 3c-1.333 1 -2 2.5 -2 4.5c0 3 2 4.5 2 4.5s2 1.5 2 4.5c0 2 -.667 3.5 -2 4.5"
|
||||
id="path1"
|
||||
style="stroke:#ffffff;stroke-opacity:1;fill:none;fill-opacity:1" />
|
||||
<path
|
||||
d="M19.794 16.5c-.2 -1.655 -1.165 -2.982 -2.897 -3.982c-2.597 -1.5 -4.897 -.518 -4.897 -.518s-2.299 .982 -4.897 -.518c-1.732 -1 -2.698 -2.327 -2.897 -3.982"
|
||||
id="path2"
|
||||
style="stroke:#ffffff;stroke-opacity:1" />
|
||||
<path
|
||||
d="M19.794 7.5c-1.532 -.655 -3.165 -.482 -4.897 .518c-2.597 1.5 -2.897 3.982 -2.897 3.982s-.299 2.482 -2.897 3.982c-1.732 1 -3.365 1.173 -4.897 .518"
|
||||
id="path3"
|
||||
style="stroke:#ffffff;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -5,6 +5,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
BANNER=$(cat << "EOF"
|
||||
__.,,.__
|
||||
:^7J5GB##&&##GPY?~:
|
||||
^75B&@@@@@@&&&@@@@@@@#GJ~:
|
||||
5&@@@&B5?7~^^^^^~!7YP#@@@@#!
|
||||
|
||||
+265
-58
@@ -2,6 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
BANNER=$(cat << "EOF"
|
||||
__.,,.__
|
||||
:^7J5GB##&&##GPY?~:
|
||||
^75B&@@@@@@&&&@@@@@@@#GJ~:
|
||||
5&@@@&B5?7~^^^^^~!7YP#@@@@#!
|
||||
@@ -65,7 +66,11 @@ else
|
||||
TPM_ENABLED=true
|
||||
fi
|
||||
|
||||
# Install basic packages first
|
||||
# Upgrade system packages first
|
||||
echo "[+] Upgrading system packages..."
|
||||
dnf upgrade -y || echo "WARNING: System upgrade failed"
|
||||
|
||||
# Install basic packages
|
||||
echo "[+] Installing basic packages..."
|
||||
dnf install -y epel-release || exit 1
|
||||
dnf config-manager --set-enabled crb || exit 1
|
||||
@@ -80,49 +85,160 @@ chmod 700 /home/${ALMA_USER}/.ssh
|
||||
chmod 600 /home/${ALMA_USER}/.ssh/authorized_keys
|
||||
chown -R ${ALMA_USER}:${ALMA_USER} /home/${ALMA_USER}/.ssh
|
||||
|
||||
# Install oh-my-zsh and powerlevel10k
|
||||
echo "[+] Installing oh-my-zsh and powerlevel10k..."
|
||||
# Download and run oh-my-zsh installer as the user with proper environment
|
||||
su - ${ALMA_USER} -c 'export RUNZSH=no CHSH=no KEEP_ZSHRC=yes && bash -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"' 2>/dev/null || echo "WARNING: oh-my-zsh installation failed"
|
||||
# Clone powerlevel10k theme
|
||||
su - ${ALMA_USER} -c 'git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/.oh-my-zsh/custom/themes/powerlevel10k' 2>/dev/null || echo "WARNING: powerlevel10k installation failed"
|
||||
# Configure passwordless sudo
|
||||
echo "[+] Configuring passwordless sudo for ${ALMA_USER}..."
|
||||
echo "${ALMA_USER} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/99-${ALMA_USER}
|
||||
chmod 440 /etc/sudoers.d/99-${ALMA_USER}
|
||||
|
||||
# Install dotfiles from git repo (cloning needed as we're in chroot)
|
||||
echo "[+] Installing dotfiles..."
|
||||
su - ${ALMA_USER} -c '
|
||||
cd &&
|
||||
git clone https://git.dominik-roth.eu/dodox/nullpoint.git /tmp/nullpoint-dotfiles &&
|
||||
cd /tmp/nullpoint-dotfiles/dotfiles &&
|
||||
# Setup terminal environment for both user and root
|
||||
echo "[+] Setting up terminal environment for ${ALMA_USER} and root..."
|
||||
|
||||
for user_account in "${ALMA_USER}" "root"; do
|
||||
echo " - Setting up $user_account..."
|
||||
|
||||
if [ "$user_account" = "root" ]; then
|
||||
home_dir="/root"
|
||||
user_prefix=""
|
||||
else
|
||||
home_dir="/home/${user_account}"
|
||||
user_prefix="su - ${user_account} -c"
|
||||
fi
|
||||
|
||||
# Install oh-my-zsh
|
||||
if [ "$user_account" = "root" ]; then
|
||||
export RUNZSH=no CHSH=no KEEP_ZSHRC=yes && bash -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" 2>/dev/null || echo "WARNING: $user_account oh-my-zsh installation failed"
|
||||
else
|
||||
su - ${user_account} -c 'export RUNZSH=no CHSH=no KEEP_ZSHRC=yes && bash -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"' 2>/dev/null || echo "WARNING: $user_account oh-my-zsh installation failed"
|
||||
fi
|
||||
|
||||
# Install powerlevel10k
|
||||
if [ "$user_account" = "root" ]; then
|
||||
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${home_dir}/.oh-my-zsh/custom/themes/powerlevel10k 2>/dev/null || echo "WARNING: $user_account powerlevel10k installation failed"
|
||||
else
|
||||
su - ${user_account} -c 'git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/.oh-my-zsh/custom/themes/powerlevel10k' 2>/dev/null || echo "WARNING: $user_account powerlevel10k installation failed"
|
||||
fi
|
||||
|
||||
# Install dotfiles
|
||||
if [ "$user_account" = "root" ]; then
|
||||
cd ${home_dir} &&
|
||||
git clone https://git.dominik-roth.eu/dodox/nullpoint.git /tmp/nullpoint-dotfiles-${user_account} &&
|
||||
cd /tmp/nullpoint-dotfiles-${user_account}/dotfiles &&
|
||||
for file in .*; do
|
||||
if [ -f "$file" ] && [ "$file" != "." ] && [ "$file" != ".." ]; then
|
||||
cp "$file" ~/ 2>/dev/null || true
|
||||
cp "$file" ${home_dir}/ 2>/dev/null || true
|
||||
fi
|
||||
done &&
|
||||
cd && rm -rf /tmp/nullpoint-dotfiles
|
||||
' || echo "WARNING: dotfiles installation failed"
|
||||
cd ${home_dir} && rm -rf /tmp/nullpoint-dotfiles-${user_account} || echo "WARNING: $user_account dotfiles installation failed"
|
||||
else
|
||||
su - ${user_account} -c "
|
||||
cd &&
|
||||
git clone https://git.dominik-roth.eu/dodox/nullpoint.git /tmp/nullpoint-dotfiles-${user_account} &&
|
||||
cd /tmp/nullpoint-dotfiles-${user_account}/dotfiles &&
|
||||
for file in .*; do
|
||||
if [ -f \"\$file\" ] && [ \"\$file\" != \".\" ] && [ \"\$file\" != \"..\" ]; then
|
||||
cp \"\$file\" ~/ 2>/dev/null || true
|
||||
fi
|
||||
done &&
|
||||
cd && rm -rf /tmp/nullpoint-dotfiles-${user_account}
|
||||
" || echo "WARNING: $user_account dotfiles installation failed"
|
||||
fi
|
||||
|
||||
# Set zsh as default shell
|
||||
if [ "$user_account" = "root" ]; then
|
||||
sed -i 's|^root:.*:/bin/bash$|root:x:0:0:root:/root:/bin/zsh|' /etc/passwd
|
||||
else
|
||||
sed -i "s|^${user_account}:.*:/bin/bash$|${user_account}:x:$(id -u ${user_account}):$(id -g ${user_account})::/home/${user_account}:/bin/zsh|" /etc/passwd
|
||||
fi
|
||||
done
|
||||
|
||||
# Set up MOTD
|
||||
if [ "$ENABLE_MOTD" = true ]; then
|
||||
echo "[+] Setting up MOTD..."
|
||||
cat > /etc/motd << MOTD
|
||||
|
||||
$BANNER
|
||||
MOTD
|
||||
fi
|
||||
|
||||
# Modify /etc/os-release to show nullpoint branding
|
||||
echo "[+] Updating /etc/os-release with nullpoint branding..."
|
||||
if [ -f /etc/os-release ]; then
|
||||
# Backup original
|
||||
cp /etc/os-release /etc/os-release.bak
|
||||
|
||||
# Get original PRETTY_NAME value
|
||||
ORIG_PRETTY_NAME=$(grep '^PRETTY_NAME=' /etc/os-release | cut -d'"' -f2)
|
||||
|
||||
# Update PRETTY_NAME to show nullpoint with base OS info
|
||||
sed -i "s/^PRETTY_NAME=.*/PRETTY_NAME=\"nullpoint (base: ${ORIG_PRETTY_NAME})\"/" /etc/os-release
|
||||
|
||||
echo " - Updated PRETTY_NAME to 'nullpoint (base: ${ORIG_PRETTY_NAME})'"
|
||||
fi
|
||||
|
||||
# Install additional packages
|
||||
echo "[+] Installing additional packages..."
|
||||
dnf install -y \
|
||||
clevis clevis-luks tpm2-tools tpm2-tss \
|
||||
tmux neovim python3-pip \
|
||||
tree gcc make autoconf automake tar bzip2 || exit 1
|
||||
tmux neovim python3-pip python3.13 python3.13-pip \
|
||||
tree gcc make autoconf automake tar bzip2 \
|
||||
bash-completion || exit 1
|
||||
|
||||
|
||||
# Install dropbear for early boot SSH
|
||||
echo "[+] Installing dropbear for early boot SSH..."
|
||||
dnf install -y dropbear dracut-network || exit 1
|
||||
|
||||
# Install lsd and bat
|
||||
echo "[+] Installing lsd and bat..."
|
||||
# Install modern CLI tools
|
||||
echo "[+] Installing lsd, bat, and fastfetch..."
|
||||
|
||||
# Try to install fastfetch from repos first
|
||||
dnf install -y fastfetch || echo "fastfetch not available in repos, skipping"
|
||||
|
||||
# Create nullpoint logo file for fastfetch
|
||||
echo "[+] Creating nullpoint logo file..."
|
||||
cat > /etc/nullpoint-logo << 'EOF'
|
||||
__.,,.__
|
||||
:^7J5GB##&&##GPY?~:
|
||||
^75B&@@@@@@&&&@@@@@@@#GJ~:
|
||||
5&@@@&B5?7~^^^^^~!7YP#@@@@#!
|
||||
Y##P7^ :~JB#B!
|
||||
:: :
|
||||
7PP?: :^~!!~^: :?PP7
|
||||
:B@@B: !5B&@@@@&B5! :#@@B:
|
||||
:!!: ^G@@@&BPPB@@@@G^ :!!:
|
||||
:B@@@5^ ^5@@@B:
|
||||
:7J7: !@@@# :&@@@~ :?J7:
|
||||
J@@@5 :#@@@Y: :Y@@@B: 5@@@J
|
||||
!@@@&^ ~B@@@&G55G&@@@B~ ~&@@@~
|
||||
5@@@G: :7P#@@@@@@#P7: :B@@@Y
|
||||
:P@@@B~ :~!77!~: ~B@@@P
|
||||
Y@@@&Y^ ^5@@@@J
|
||||
!G@@@&P7^ ^7P&@@@G~
|
||||
!P&@@@&B? :: ?B&@@@&P!
|
||||
^75#&&Y :P&&5: 5&&B57^
|
||||
:^^ :P&&5: ^^:
|
||||
^^
|
||||
EOF
|
||||
|
||||
# Install user-specific tools for both user and root
|
||||
echo "[+] Installing user-specific tools..."
|
||||
# Install for user
|
||||
echo " - Installing for ${ALMA_USER}..."
|
||||
su - ${ALMA_USER} -c 'curl -fsSL https://claude.ai/install.sh | bash' || echo "WARNING: ${ALMA_USER} Claude Code installation failed"
|
||||
su - ${ALMA_USER} -c 'python3.13 -m pip install --user bpytop' || echo "WARNING: ${ALMA_USER} bpytop installation failed"
|
||||
|
||||
# Install for root
|
||||
echo " - Installing for root..."
|
||||
curl -fsSL https://claude.ai/install.sh | bash || echo "WARNING: root Claude Code installation failed"
|
||||
python3.13 -m pip install bpytop || echo "WARNING: root bpytop installation failed"
|
||||
|
||||
# Install Docker from official repository
|
||||
echo "[+] Installing Docker..."
|
||||
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo || echo "WARNING: Docker repo setup failed"
|
||||
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || echo "WARNING: Docker installation failed"
|
||||
systemctl enable docker || echo "WARNING: Docker enable failed"
|
||||
usermod -aG docker ${ALMA_USER} || echo "WARNING: Adding user to docker group failed"
|
||||
|
||||
# Install using fixed versions that should work
|
||||
LSD_VERSION="1.0.0"
|
||||
BAT_VERSION="0.24.0"
|
||||
@@ -148,9 +264,10 @@ man "$@"
|
||||
BATMAN
|
||||
chmod +x /usr/local/bin/batman
|
||||
|
||||
# Create .tmp directory for user
|
||||
# Create .tmp directory for user and root
|
||||
mkdir -p /home/${ALMA_USER}/.tmp
|
||||
chown ${ALMA_USER}:${ALMA_USER} /home/${ALMA_USER}/.tmp
|
||||
mkdir -p /root/.tmp
|
||||
|
||||
# Configure Clevis for automatic unlock
|
||||
if [ ${#TANG_SERVERS[@]} -gt 0 ] || [ "$TPM_ENABLED" = true ]; then
|
||||
@@ -229,14 +346,12 @@ install() {
|
||||
inst /etc/dropbear/authorized_keys /root/.ssh/authorized_keys
|
||||
fi
|
||||
|
||||
# Generate host keys if they don't exist
|
||||
for keytype in rsa ecdsa ed25519; do
|
||||
keyfile="/etc/dropbear/dropbear_${keytype}_host_key"
|
||||
# Install ED25519 host key only
|
||||
keyfile="/etc/dropbear/dropbear_ed25519_host_key"
|
||||
if [ ! -f "$keyfile" ]; then
|
||||
dropbearkey -t $keytype -f "$keyfile" 2>/dev/null
|
||||
dropbearkey -t ed25519 -f "$keyfile" 2>/dev/null
|
||||
fi
|
||||
[ -f "$keyfile" ] && inst "$keyfile"
|
||||
done
|
||||
|
||||
# Install the service
|
||||
inst_simple "$moddir/dropbear.service" /etc/systemd/system/dropbear.service
|
||||
@@ -271,15 +386,45 @@ cat > /usr/lib/dracut/modules.d/60dropbear-ssh/unlock-luks.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
echo "=== LUKS Remote Unlock Helper ==="
|
||||
echo ""
|
||||
echo "Available block devices:"
|
||||
lsblk -o NAME,SIZE,TYPE,FSTYPE
|
||||
echo "Checking for encrypted devices..."
|
||||
|
||||
# Show block devices if available
|
||||
if command -v lsblk >/dev/null 2>&1; then
|
||||
echo "Block devices:"
|
||||
lsblk -o NAME,SIZE,TYPE,FSTYPE 2>/dev/null || echo " (lsblk not available)"
|
||||
else
|
||||
echo "Block devices: (listing /dev/sd* and /dev/md*)"
|
||||
ls -la /dev/sd* /dev/md* 2>/dev/null || echo " No standard devices found"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Encrypted devices waiting for unlock:"
|
||||
systemd-ask-password --list
|
||||
echo "Encrypted devices status:"
|
||||
# Check for LUKS devices waiting to be unlocked
|
||||
for dev in /dev/mapper/luks-*; do
|
||||
if [ -e "$dev" ]; then
|
||||
echo " Found: $dev"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check systemd-ask-password files directly
|
||||
if [ -d /run/systemd/ask-password ]; then
|
||||
echo ""
|
||||
echo "Password prompts waiting:"
|
||||
ls -la /run/systemd/ask-password/ 2>/dev/null
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "To unlock, run: systemd-tty-ask-password-agent"
|
||||
echo "Starting unlock process..."
|
||||
echo "Enter your LUKS passphrase when prompted:"
|
||||
echo ""
|
||||
exec systemd-tty-ask-password-agent
|
||||
|
||||
# Run the password agent
|
||||
if command -v systemd-tty-ask-password-agent >/dev/null 2>&1; then
|
||||
systemd-tty-ask-password-agent
|
||||
else
|
||||
echo "ERROR: systemd-tty-ask-password-agent not found!"
|
||||
echo "Try running: /lib/systemd/systemd-tty-ask-password-agent"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x /usr/lib/dracut/modules.d/60dropbear-ssh/*.sh
|
||||
@@ -289,26 +434,43 @@ mkdir -p /etc/dropbear
|
||||
echo "${SSH_KEY}" > /etc/dropbear/authorized_keys
|
||||
chmod 600 /etc/dropbear/authorized_keys
|
||||
|
||||
# Generate host keys and display SHA256 fingerprints
|
||||
echo "[+] Generating SSH host keys..."
|
||||
for keytype in rsa ecdsa ed25519; do
|
||||
keyfile="/etc/dropbear/dropbear_${keytype}_host_key"
|
||||
if [ ! -f "$keyfile" ]; then
|
||||
echo " - Generating $keytype key..."
|
||||
dropbearkey -t $keytype -f "$keyfile" | grep -v "Generating" || true
|
||||
# Generate ED25519 host key only (most secure)
|
||||
echo "[+] Generating ED25519 SSH host key..."
|
||||
|
||||
# Extract and display SHA256 fingerprint for ed25519
|
||||
if [ "$keytype" = "ed25519" ] && command -v ssh-keygen >/dev/null 2>&1; then
|
||||
# Convert dropbear key to OpenSSH format and get SHA256 fingerprint
|
||||
dropbearkey -y -f "$keyfile" | grep "^ssh-" > "/tmp/dropbear_${keytype}.pub"
|
||||
fingerprint=$(ssh-keygen -lf "/tmp/dropbear_${keytype}.pub" -E sha256 2>/dev/null | awk '{print $2}')
|
||||
# Use system SSH key if available, otherwise generate dropbear key
|
||||
openssh_key="/etc/ssh/ssh_host_ed25519_key"
|
||||
dropbear_key="/etc/dropbear/dropbear_ed25519_host_key"
|
||||
|
||||
if [ -f "$openssh_key" ] && command -v dropbearconvert >/dev/null 2>&1; then
|
||||
echo " Converting existing OpenSSH ED25519 key to dropbear format..."
|
||||
dropbearconvert openssh dropbear "$openssh_key" "$dropbear_key" 2>/dev/null || {
|
||||
echo " Conversion failed, generating new dropbear key..."
|
||||
dropbearkey -t ed25519 -f "$dropbear_key" | grep -v "Generating" || true
|
||||
}
|
||||
elif [ ! -f "$dropbear_key" ]; then
|
||||
echo " Generating new ED25519 key..."
|
||||
dropbearkey -t ed25519 -f "$dropbear_key" | grep -v "Generating" || true
|
||||
|
||||
# Also generate OpenSSH format to prevent key mismatch after boot
|
||||
if command -v ssh-keygen >/dev/null 2>&1; then
|
||||
echo " Generating matching OpenSSH key..."
|
||||
mkdir -p /etc/ssh
|
||||
# Extract public key and generate OpenSSH private key
|
||||
dropbearkey -y -f "$dropbear_key" | grep "^ssh-" > "${openssh_key}.pub"
|
||||
# Note: Direct conversion from dropbear to openssh private key requires dropbearconvert
|
||||
# For now, we'll have different keys but document the solution
|
||||
fi
|
||||
fi
|
||||
|
||||
# Display SHA256 fingerprint
|
||||
if command -v ssh-keygen >/dev/null 2>&1; then
|
||||
fingerprint=$(dropbearkey -y -f "$dropbear_key" | ssh-keygen -lf - -E sha256 2>/dev/null | awk '{print $2}')
|
||||
if [ -n "$fingerprint" ]; then
|
||||
echo " - ED25519 SHA256 fingerprint: $fingerprint"
|
||||
echo " SHA256 fingerprint: $fingerprint"
|
||||
echo " Note: This is the initramfs (rescue) SSH fingerprint."
|
||||
echo " The normal system SSH may have a different fingerprint."
|
||||
fi
|
||||
rm -f "/tmp/dropbear_${keytype}.pub"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Configure dracut
|
||||
cat > /etc/dracut.conf.d/60-dropbear-ssh.conf << 'EOF'
|
||||
@@ -327,9 +489,24 @@ echo "[+] Enabling services..."
|
||||
# systemctl enable stratisd # Not needed without Stratis
|
||||
systemctl enable sshd
|
||||
|
||||
# Disable root login
|
||||
# Secure SSH configuration
|
||||
echo "[+] Securing SSH..."
|
||||
sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||
{
|
||||
# Disable root login
|
||||
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||
|
||||
# Only allow SSH key authentication
|
||||
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
||||
sed -i 's/^#*ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config
|
||||
sed -i 's/^#*UsePAM.*/UsePAM no/' /etc/ssh/sshd_config
|
||||
|
||||
# Only allow specific user
|
||||
echo "AllowUsers ${ALMA_USER}" >> /etc/ssh/sshd_config
|
||||
|
||||
echo " - Root login disabled"
|
||||
echo " - Password authentication disabled"
|
||||
echo " - Only user '${ALMA_USER}' allowed"
|
||||
}
|
||||
|
||||
# Set SELinux to enforcing
|
||||
echo "[+] Setting SELinux to enforcing..."
|
||||
@@ -337,11 +514,41 @@ sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
|
||||
|
||||
echo "✅ Post-installation complete!"
|
||||
echo ""
|
||||
echo "IMPORTANT: The LUKS passphrase is set in install.conf"
|
||||
echo "Save it securely for recovery purposes."
|
||||
|
||||
# Display SSH host key fingerprints
|
||||
echo "SSH Host Key Fingerprints:"
|
||||
ed25519_fp=""
|
||||
dropbear_fp=""
|
||||
|
||||
if [ -f "/etc/ssh/ssh_host_ed25519_key.pub" ] && command -v ssh-keygen >/dev/null 2>&1; then
|
||||
ed25519_fp=$(ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub -E sha256 2>/dev/null | awk '{print $2}')
|
||||
fi
|
||||
|
||||
if [ -f "/etc/dropbear/dropbear_ed25519_host_key" ] && command -v ssh-keygen >/dev/null 2>&1; then
|
||||
dropbear_fp=$(dropbearkey -y -f /etc/dropbear/dropbear_ed25519_host_key 2>/dev/null | ssh-keygen -lf - -E sha256 2>/dev/null | awk '{print $2}')
|
||||
fi
|
||||
|
||||
if [ -n "$ed25519_fp" ] && [ -n "$dropbear_fp" ] && [ "$ed25519_fp" = "$dropbear_fp" ]; then
|
||||
echo " SSH (ED25519): $ed25519_fp (same for both rescue and normal)"
|
||||
elif [ -n "$ed25519_fp" ] && [ -n "$dropbear_fp" ]; then
|
||||
echo " Normal SSH (ED25519): $ed25519_fp"
|
||||
echo " Rescue SSH (ED25519): $dropbear_fp"
|
||||
elif [ -n "$ed25519_fp" ]; then
|
||||
echo " Normal SSH (ED25519): $ed25519_fp"
|
||||
elif [ -n "$dropbear_fp" ]; then
|
||||
echo " Rescue SSH (ED25519): $dropbear_fp"
|
||||
else
|
||||
echo " No ED25519 keys found"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "After reboot:"
|
||||
echo "- SSH to port 22 for remote unlock: ssh root@<server-ip>"
|
||||
echo "- Run 'unlock-luks' and follow the instructions to unlock LUKS"
|
||||
echo "- Once unlocked, SSH to port 22 as user '${ALMA_USER}'"
|
||||
echo "- LUKS passphrase: [see installer output]"
|
||||
echo "IMPORTANT: Save the LUKS passphrase from install.conf securely!"
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo "1. Manually reboot the system when ready"
|
||||
echo "2. SSH root@<server-ip> → run 'unlock-luks' → enter passphrase"
|
||||
echo "3. System finalizes setup and reboots automatically"
|
||||
echo "4. SSH root@<server-ip> → run 'unlock-luks' → enter passphrase again"
|
||||
echo "5. System is ready → SSH as user '${ALMA_USER}'"
|
||||
echo ""
|
||||
echo "Future boots: Only one unlock needed (or automatic if Tang/Clevis configured)"
|
||||
Reference in New Issue
Block a user