diff --git a/ct/alpine-step-ca.sh b/ct/alpine-step-ca.sh new file mode 100644 index 00000000..fd349194 --- /dev/null +++ b/ct/alpine-step-ca.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: FWiegerinck +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/smallstep/certificates + +APP="Alpine-Step-CA" +var_tags="alpine;step-ca" +var_cpu="1" +var_ram="512" +var_disk="1024" +var_os="alpine" +var_version="3.20" +var_unprivileged="1" + +DEFAULT_CA_NAME="HomeLab CA" + +header_info "$APP" +base_settings +variables +color +catch_errors + +function update_script() { + if ! apk -e info newt >/dev/null 2>&1; then + apk add -q newt + fi + while true; do + CHOICE=$( + whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --menu "Select option" 11 58 1 \ + "1" "Check for Step CA Updates" 3>&2 2>&1 1>&3 + ) + exit_status=$? + if [ $exit_status == 1 ]; then + clear + exit-script + fi + header_info + case $CHOICE in + 1) + apk update && apk upgrade + exit + ;; + esac + done +} + +function ca_settings() { + + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Configure Certificate Authority" "Now that we defined the container we need to configure the certificate authority." 8 58 + + if CA_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Name of certificate authority" 8 58 "$DEFAULT_CA_NAME" --title "Configure Certificate Authority" 3>&1 1>&2 2>&3); then + if [ -z "$CA_NAME" ]; then + CA_NAME="$DEFAULT_CA_NAME" + fi + else + exit + fi + + CA_DNS_ENTRIES=() + DEFAULT_CA_DNS_ENTRY="${HN}.local" + if CA_PRIMARY_DNS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "DNS entry of Certificate Authority" 8 58 "$DEFAULT_CA_DNS_ENTRY" --title "Configure Certificate Authority" 3>&1 1>&2 2>&3); then + if [ -z "$CA_PRIMARY_DNS" ]; then + CA_PRIMARY_DNS=$DEFAULT_CA_DNS_ENTRY + fi + CA_DNS_ENTRIES+=("--dns=$CA_PRIMARY_DNS") + else + exit + fi + + while whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Configure Certificate Authority" --yesno "Do you want to add another DNS entry?" 10 72 ; do + if dns_entry=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "DNS entry of Certificate Authority" 8 58 "" --title "Configure Certificate Authority" 3>&1 1>&2 2>&3); then + if [ -n "$dns_entry" ]; then + CA_DNS_ENTRIES+=(" --dns=$dns_entry") + fi + fi + done + + x509_policy_dns=() + while true; do + if dns_entry=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "[X509 Policy] Allowed by DNS. Use full ('domain.local') or wildcard ('*.local') DNS:" 8 58 "" --title "Configure Certificate Authority" 3>&1 1>&2 2>&3); then + if [ -n "$dns_entry" ]; then + x509_policy_dns+=("$dns_entry") + else + break + fi + else + exit + fi + done + + x509_policy_ips=() + while true; do + if ip_entry=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "[X509 Policy] Allowed by IP addresses. Use single address ('192.168.1.169' or '::1') or CIDR address ranges ('192.168.1.0/24' or '2001:0db8::/120'):" 8 58 "" --title "Configure Certificate Authority" 3>&1 1>&2 2>&3); then + if [ -n "$ip_entry" ]; then + x509_policy_ips+=("$ip_entry") + else + break + fi + else + exit + fi + done + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Configure Certificate Authority" --yesno "Enable ACME?" 10 58); then + CA_ACME="yes" + + default_ca_acme_name="acme" + if CA_ACME_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Name of ACME provider" 8 58 "$default_ca_acme_name" --title "Configure Certificate Authority" 3>&1 1>&2 2>&3); then + if [ -z "$CA_ACME_NAME" ]; then + CA_ACME_NAME="$default_ca_acme_name" + fi + else + exit + fi + + else + CA_ACME="no" + fi + + if [ "$VERBOSE" = "yes" ]; then + echo -e "${DEFAULT}${BOLD}${DGN}Name of CA: ${BGN}$CA_NAME${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}DNS entries of CA:${CL}" + for DNS_ENTRY in ${CA_DNS_ENTRIES[*]}; do + echo -e " - $DNS_ENTRY" + done + echo -e "${DEFAULT}${BOLD}${DGN}X509 Policy - allow:{CL}" + echo -e " - DNS entries: ${x509_policy_dns[*]}" + echo -e " - IP addresses: ${x509_policy_ips[*]}" + + echo -e "${DEFAULT}${BOLD}${DGN}Enable ACME: ${BGN}$CA_ACME${CL}" + if [ "${CA_ACME}" = "yes" ]; then + echo -e " - Name of provider: ${CA_ACME_NAME}" + fi + fi + + export CA_NAME + export CA_PRIMARY_DNS + export CA_DNS=${CA_DNS_ENTRIES[*]} + export CA_X509_POLICY_DNS=${x509_policy_dns[*]} + export CA_X509_POLICY_IPS=${x509_policy_ips[*]} + export CA_ACME + export CA_ACME_NAME +} + +start +ca_settings +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +if [ "${CA_ACME}" = "yes" ]; then + echo -e " ACME should be reachable at URL: https://${CA_PRIMARY_DNS}/acme/{$CA_ACME_NAME}/directory" +fi diff --git a/install/alpine-step-ca-install.sh b/install/alpine-step-ca-install.sh new file mode 100644 index 00000000..aeb5fbb5 --- /dev/null +++ b/install/alpine-step-ca-install.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: FWiegerinck +# License: MIT +# Source: https://github.com/smallstep/certificates + +# Import Functions und Setup +source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +motd_ssh +customize + +config_dir="/etc/step-ca" +passwd_file="${config_dir}/password.txt" +ca_admin_provisioner="Admin JWK" +ca_admin_subject="admin-localhost" +ca_admin_provisioner_passwd_file="${config_dir}/admin-jwk-password.txt" + +msg_info "Installing dependencies" +$STD apk add newt +$STD apk add openssl +msg_ok "Installed dependencies" + +msg_info "Preparing environment" +$STD echo "export STEPPATH=/etc/step-ca" >> ~/.profile +$STD export STEPPATH=/etc/step-ca +msg_ok "Environment prepared" + +msg_info "Installing Alpine Step-CA" +$STD apk add step-cli step-certificates +msg_ok "Installed Alpine Step-CA" + +msg_info "Generate CA secrets" + +function generatePasswordFile(){ + $STD echo "$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" > "$1" + chmod 600 "$1" +} + +generatePasswordFile "${passwd_file}" +generatePasswordFile "${ca_admin_provisioner_passwd_file}" + +msg_ok "Generated CA secrets" + +msg_info "Initialize base CA" + +$STD step ca init --name "${CA_NAME}" --dns localhost $CA_DNS --password-file ${passwd_file} --deployment-type standalone --address ":443" --provisioner "${ca_admin_provisioner}" --admin-subject "${ca_admin_subject}" --provisioner-password-file ${ca_admin_provisioner_passwd_file} --remote-management +$STD rc-service step-ca start + +timeout_counter=0 +while ! nc -z localhost 443; do + sleep 0.5 + + ((timeout_counter=timeout_counter+1)) + if (( timeout_counter > 30 )); then + msg_error "Failed to start Step-CA" + exit + fi +done + +msg_ok "Initialized base CA" + +if [ -n "${CA_X509_POLICY_DNS}" ] || [ -n "${CA_X509_POLICY_IPS}" ]; then + msg_info "Configure CA policy" + + $STD step ca policy authority x509 allow dns "${ca_admin_subject}" --admin-provisioner "${ca_admin_provisioner}" --admin-subject "${ca_admin_subject}" --password-file ${ca_admin_provisioner_passwd_file} + + if [ -n "${CA_X509_POLICY_DNS}" ]; then + $STD step ca policy authority x509 allow dns ${CA_X509_POLICY_DNS} --admin-provisioner "${ca_admin_provisioner}" --admin-subject "${ca_admin_subject}" --password-file ${ca_admin_provisioner_passwd_file} + fi + if [ -n "${CA_X509_POLICY_IPS}" ]; then + $STD step ca policy authority x509 allow ip ${CA_X509_POLICY_IPS} --admin-provisioner "${ca_admin_provisioner}" --admin-subject "${ca_admin_subject}" --password-file ${ca_admin_provisioner_passwd_file} + fi + + msg_ok "Configured CA policy" +fi + +if [ "${CA_ACME}" = "yes" ]; then + msg_info "Initialize ACME for CA" + $STD step ca provisioner add "${CA_ACME_NAME}" --type ACME --x509-min-dur=20m --x509-max-dur=32h --x509-default-dur=24h --admin-provisioner "${ca_admin_provisioner}" --admin-subject "${ca_admin_subject}" --password-file ${ca_admin_provisioner_passwd_file} + msg_ok "Initialized ACME for CA" +fi + +msg_info "Starting Alpine Step-CA" +$STD rc-service step-ca restart +$STD rc-update add step-ca default +msg_ok "Started Alpine Step-CA" + +msg_ok "Completed setup of CA" + +ca_root_fingerprint=$(step certificate fingerprint ${STEPPATH}/certs/root_ca.crt) +$STD echo "echo \"Fingerprint CA Root Certificate: ${ca_root_fingerprint}\" " >> ~/.profile \ No newline at end of file diff --git a/json/step-ca.json b/json/step-ca.json new file mode 100644 index 00000000..ed55de2e --- /dev/null +++ b/json/step-ca.json @@ -0,0 +1,34 @@ +{ + "name": "Step CA", + "slug": "step-ca", + "categories": [ + 11 + ], + "date_created": "2025-01-10", + "type": "ct", + "updateable": false, + "privileged": false, + "interface_port": 443, + "documentation": "https://smallstep.com/docs/step-ca/configuration/", + "website": "https://smallstep.com/docs/step-ca/", + "logo": "https://avatars.githubusercontent.com/u/23183426?v=4", + "description": "Step CA is an open source Certificate Authority provided by Smallstep. This script creates a step-ca instance as your local online Certificate Authority with ACME support. It provides secure, automated X.509 and SSH certificate management. ", + "install_methods": [ + { + "type": "alpine", + "script": "/ct/alpine-step-ca.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 1, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] + } \ No newline at end of file