diff --git a/setup.sh b/setup.sh index 96a4ab69..d1468332 100755 --- a/setup.sh +++ b/setup.sh @@ -36,7 +36,18 @@ configure_apt_cacher() { echo "Configuring apt-cacher..." sudo chmod +x ./apt-cacher/setup.sh ./apt-cacher/setup.sh -} +} + +install_node_if_no_gui() { + if [ -z "$XDG_CURRENT_DESKTOP" ]; then + echo "No GUI detected, installing Node..." + sudo chmod +x ./teleport/install-node.sh + ./teleport/install-node.sh + else + echo "GUI detected, skipping Node installation." + fi +} configure_terminal configure_apt_cacher +install_node_if_no_gui diff --git a/teleport/install-node.sh b/teleport/install-node.sh new file mode 100644 index 00000000..74783033 --- /dev/null +++ b/teleport/install-node.sh @@ -0,0 +1,1009 @@ +#!/bin/bash +set -euo pipefail +SCRIPT_NAME="teleport-installer" + +# default values +ALIVE_CHECK_DELAY=3 +CONNECTIVITY_TEST_METHOD="" +COPY_COMMAND="cp" +DISTRO_TYPE="" +IGNORE_CONNECTIVITY_CHECK="${TELEPORT_IGNORE_CONNECTIVITY_CHECK:-false}" +LAUNCHD_CONFIG_PATH="/Library/LaunchDaemons" +LOG_FILENAME="$(mktemp -t ${SCRIPT_NAME}.log.XXXXXXXXXX)" +MACOS_STDERR_LOG="/var/log/teleport-stderr.log" +MACOS_STDOUT_LOG="/var/log/teleport-stdout.log" +SYSTEMD_UNIT_PATH="/lib/systemd/system/teleport.service" +TARGET_PORT_DEFAULT=443 +TELEPORT_ARCHIVE_PATH='teleport' +TELEPORT_BINARY_DIR="/usr/local/bin" +TELEPORT_BINARY_LIST="teleport tctl tsh" +TELEPORT_CONFIG_PATH="/etc/teleport.yaml" +TELEPORT_DATA_DIR="/var/lib/teleport" +TELEPORT_DOCS_URL="https://goteleport.com/docs/" +TELEPORT_FORMAT="" + +# initialise variables (because set -u disallows unbound variables) +f="" +l="" +DISABLE_TLS_VERIFICATION=false +NODENAME=$(hostname) +IGNORE_CHECKS=false +OVERRIDE_FORMAT="" +QUIET=false +APP_INSTALL_DECISION="" +INTERACTIVE=false + +# the default value of each variable is a templatable Go value so that it can +# optionally be replaced by the server before the script is served up +TELEPORT_VERSION='13.4.26' +TELEPORT_PACKAGE_NAME='teleport' +REPO_CHANNEL='' +TARGET_HOSTNAME='dev.bramkelchtermans.be' +TARGET_PORT='443' +JOIN_TOKEN='70649dc2f5d16138ef278c368aeac0a3' +JOIN_METHOD='' +JOIN_METHOD_FLAG="" +[ -n "$JOIN_METHOD" ] && JOIN_METHOD_FLAG="--join-method ${JOIN_METHOD}" + +# inject labels into the configuration +LABELS='teleport.internal/resource-id=d082b3d8-e775-4572-ad6c-473b131c739e' +LABELS_FLAG=() +[ -n "$LABELS" ] && LABELS_FLAG=(--labels "${LABELS}") + +# When all stanza generators have been updated to use the new +# `teleport configure` commands CA_PIN_HASHES can be removed along +# with the script passing it in in `join_tokens.go`. +CA_PIN_HASHES='sha256:cb88c2b06d5a1a6dbfb62ea77e0c968e71c6e0a0c71a791570c548622024dd7c' +CA_PINS='sha256:cb88c2b06d5a1a6dbfb62ea77e0c968e71c6e0a0c71a791570c548622024dd7c' +ARG_CA_PIN_HASHES="" +APP_INSTALL_MODE='false' +APP_NAME='' +APP_URI='' +DB_INSTALL_MODE='false' + +# usage message +# shellcheck disable=SC2086 +usage() { echo "Usage: $(basename $0) [-v teleport_version] [-h target_hostname] [-p target_port] [-j join_token] [-c ca_pin_hash]... [-q] [-l log_filename] [-a app_name] [-u app_uri] " 1>&2; exit 1; } +while getopts ":v:h:p:j:c:f:ql:ika:u:" o; do + case "${o}" in + v) TELEPORT_VERSION=${OPTARG};; + h) TARGET_HOSTNAME=${OPTARG};; + p) TARGET_PORT=${OPTARG};; + j) JOIN_TOKEN=${OPTARG};; + c) ARG_CA_PIN_HASHES="${ARG_CA_PIN_HASHES} ${OPTARG}";; + f) f=${OPTARG}; if [[ ${f} != "tarball" && ${f} != "deb" && ${f} != "rpm" ]]; then usage; fi;; + q) QUIET=true;; + l) l=${OPTARG};; + i) IGNORE_CHECKS=true; COPY_COMMAND="cp -f";; + k) DISABLE_TLS_VERIFICATION=true;; + a) APP_INSTALL_MODE=true && APP_NAME=${OPTARG};; + u) APP_INSTALL_MODE=true && APP_URI=${OPTARG};; + *) usage;; + esac +done +shift $((OPTIND-1)) + +if [[ "${ARG_CA_PIN_HASHES}" != "" ]]; then + CA_PIN_HASHES="${ARG_CA_PIN_HASHES}" +fi + +# function to construct a go template variable +# go's template parser is a bit finicky, so we dynamically build the value one character at a time +construct_go_template() { + OUTPUT="{" + OUTPUT+="{" + OUTPUT+="." + OUTPUT+="${1}" + OUTPUT+="}" + OUTPUT+="}" + echo "${OUTPUT}" +} + +# check whether we are root, exit if not +assert_running_as_root() { + if ! [ "$(id -u)" = 0 ]; then + echo "This script must be run as root." 1>&2 + exit 1 + fi +} + +# function to check whether variables are either blank or set to the default go template value +# (because they haven't been set by the go script generator or a command line argument) +# returns 1 if the variable is set to a default/zero value +# returns 0 otherwise (i.e. it needs to be set interactively) +check_variable() { + VARIABLE_VALUE="${!1}" + GO_TEMPLATE_NAME=$(construct_go_template "${2}") + if [[ "${VARIABLE_VALUE}" == "" ]] || [[ "${VARIABLE_VALUE}" == "${GO_TEMPLATE_NAME}" ]]; then + return 1 + fi + return 0 +} + +# function to check whether a provided value is "truthy" i.e. it looks like you're trying to say "yes" +is_truthy() { + declare -a TRUTHY_VALUES + TRUTHY_VALUES=("y" "Y" "yes" "YES" "ye" "YE" "yep" "YEP" "ya" "YA") + CHECK_VALUE="$1" + for ARRAY_VALUE in "${TRUTHY_VALUES[@]}"; do [[ "${CHECK_VALUE}" == "${ARRAY_VALUE}" ]] && return 0; done + return 1 +} + +# function to read input until the value you get is non-empty +read_nonblank_input() { + INPUT="" + VARIABLE_TO_ASSIGN="$1" + shift + PROMPT="$*" + until [[ "${INPUT}" != "" ]]; do + echo -n "${PROMPT}" + read -r INPUT + done + printf -v "${VARIABLE_TO_ASSIGN}" '%s' "${INPUT}" +} + +# error if we're not root +assert_running_as_root + +# set/read values interactively if not provided +# users will be prompted to enter their own value if all the following are true: +# - the current value is blank, or equal to the default Go template value +# - the value has not been provided by command line argument +! check_variable TELEPORT_VERSION version && INTERACTIVE=true && read_nonblank_input TELEPORT_VERSION "Enter Teleport version to install (without v): " +! check_variable TARGET_HOSTNAME hostname && INTERACTIVE=true && read_nonblank_input TARGET_HOSTNAME "Enter target hostname to connect to: " +! check_variable TARGET_PORT port && INTERACTIVE=true && { echo -n "Enter target port to connect to [${TARGET_PORT_DEFAULT}]: "; read -r TARGET_PORT; } +! check_variable JOIN_TOKEN token && INTERACTIVE=true && read_nonblank_input JOIN_TOKEN "Enter Teleport join token as provided: " +! check_variable CA_PIN_HASHES caPins && INTERACTIVE=true && read_nonblank_input CA_PIN_HASHES "Enter CA pin hash (separate multiple hashes with spaces): " +[ -n "${f}" ] && OVERRIDE_FORMAT=${f} +[ -n "${l}" ] && LOG_FILENAME=${l} +# if app service mode is not set (or is the default value) and we are running interactively (i.e. the user has provided some input already), +# prompt the user to choose whether to enable app_service +if [[ "${INTERACTIVE}" == "true" ]]; then + if ! check_variable APP_INSTALL_MODE appInstallMode; then + APP_INSTALL_MODE="false" + echo -n "Would you like to enable and configure Teleport's app_service, to use Teleport as a reverse proxy for a web application? [y/n, default: n] " + read -r APP_INSTALL_DECISION + if is_truthy "${APP_INSTALL_DECISION}"; then + APP_INSTALL_MODE="true" + fi + fi +fi +# prompt for extra needed values if we're running in app service mode +if [[ "${APP_INSTALL_MODE}" == "true" ]]; then + ! check_variable APP_NAME appName && read_nonblank_input APP_NAME "Enter app name to install (must be DNS-compatible; less than 63 characters, no spaces, only - or _ as punctuation): " + ! check_variable APP_URI appURI && read_nonblank_input APP_URI "Enter app URI (the host running the Teleport app service must be able to connect to this): " + # generate app public addr by concatenating values + APP_PUBLIC_ADDR="${APP_NAME}.${TARGET_HOSTNAME}" +fi + +# set default target port if value not provided +if [[ "${TARGET_PORT}" == "" ]]; then + TARGET_PORT=${TARGET_PORT_DEFAULT} +fi + +# clear log file if provided +if [[ "${LOG_FILENAME}" != "" ]]; then + if [ -f "${LOG_FILENAME}" ]; then + echo -n "" > "${LOG_FILENAME}" + fi +fi + +# log functions +log_date() { echo -n "$(date '+%Y-%m-%d %H:%M:%S %Z')"; } +log() { + LOG_LINE="$(log_date) [${SCRIPT_NAME}] $*" + if [[ ${QUIET} != "true" ]]; then + echo "${LOG_LINE}" + fi + if [[ "${LOG_FILENAME}" != "" ]]; then + echo "${LOG_LINE}" >> "${LOG_FILENAME}" + fi +} +# writes a line with no timestamp or starting data, always prints +log_only() { + LOG_LINE="$*" + echo "${LOG_LINE}" + if [[ "${LOG_FILENAME}" != "" ]]; then + echo "${LOG_LINE}" >> "${LOG_FILENAME}" + fi +} +# writes a line by itself as a header +log_header() { + LOG_LINE="$*" + echo "" + echo "${LOG_LINE}" + echo "" + if [[ "${LOG_FILENAME}" != "" ]]; then + echo "${LOG_LINE}" >> "${LOG_FILENAME}" + fi +} +# important log lines, print even when -q (quiet) is passed +log_important() { + LOG_LINE="$(log_date) [${SCRIPT_NAME}] ---> $*" + echo "${LOG_LINE}" + if [[ "${LOG_FILENAME}" != "" ]]; then + echo "${LOG_LINE}" >> "${LOG_FILENAME}" + fi +} +log_cleanup_message() { + log_only "This script does not overwrite any existing settings or Teleport installations." + log_only "Please clean up by running any of the following steps as necessary:" + log_only "- stop any running Teleport processes" + log_only " - pkill -f teleport" + log_only "- remove any data under ${TELEPORT_DATA_DIR}, along with the directory itself" + log_only " - rm -rf ${TELEPORT_DATA_DIR}" + log_only "- remove any configuration at ${TELEPORT_CONFIG_PATH}" + log_only " - rm -f ${TELEPORT_CONFIG_PATH}" + log_only "- remove any Teleport binaries (${TELEPORT_BINARY_LIST}) installed under ${TELEPORT_BINARY_DIR}" + for BINARY in ${TELEPORT_BINARY_LIST}; do EXAMPLE_DELETE_COMMAND+="${TELEPORT_BINARY_DIR}/${BINARY} "; done + log_only " - rm -f ${EXAMPLE_DELETE_COMMAND}" + log_only "Run this installer again when done." + log_only +} + +# other functions +# check whether a named program exists +check_exists() { NAME=$1; if type "${NAME}" >/dev/null 2>&1; then return 0; else return 1; fi; } +# checks for the existence of a list of named binaries and exits with error if any of them don't exist +check_exists_fatal() { + for TOOL in "$@"; do + if ! check_exists "${TOOL}"; then + log_important "Error: cannot find ${TOOL} - it needs to be installed" + exit 1 + fi + done +} +# check connectivity to the given host/port and make a request to see if Teleport is listening +# uses the global variable CONNECTIVITY_TEST_METHOD to return the name of the checker, as return +# values aren't really a thing that exists in bash +check_connectivity() { + HOST=$1 + PORT=$2 + # check with nc + if check_exists nc; then + CONNECTIVITY_TEST_METHOD="nc" + if nc -z -w3 "${HOST}" "${PORT}" >/dev/null 2>&1; then return 0; else return 1; fi + # if there's no nc, check with telnet + elif check_exists telnet; then + CONNECTIVITY_TEST_METHOD="telnet" + if echo -e '\x1dclose\x0d' | telnet "${HOST}" "${PORT}" >/dev/null 2>&1; then return 0; else return 1; fi + # if there's no nc or telnet, try and use /dev/tcp + elif [ -f /dev/tcp ]; then + CONNECTIVITY_TEST_METHOD="/dev/tcp" + if (head -1 < "/dev/tcp/${HOST}/${PORT}") >/dev/null 2>&1; then return 0; else return 1; fi + else + return 255 + fi +} +# check whether a teleport DEB is already installed and exit with error if so +check_deb_not_already_installed() { + check_exists_fatal dpkg awk + DEB_INSTALLED=$(dpkg -l | awk '{print $2}' | grep -E ^teleport || true) + if [[ ${DEB_INSTALLED} != "" ]]; then + log_important "It looks like there is already a Teleport DEB package installed (name: ${DEB_INSTALLED})." + log_important "You will need to remove that package before using this script." + exit 1 + fi +} +# check whether a teleport RPM is already installed and exit with error if so +check_rpm_not_already_installed() { + check_exists_fatal rpm + RPM_INSTALLED=$(rpm -qa | grep -E ^teleport || true) + if [[ ${RPM_INSTALLED} != "" ]]; then + log_important "It looks like there is already a Teleport RPM package installed (name: ${RPM_INSTALLED})." + log_important "You will need to remove that package before using this script." + exit 1 + fi +} +# function to check if given variable is set +check_set() { + CHECK_KEY=${1} || true + CHECK_VALUE=${!1} || true + if [[ "${CHECK_VALUE}" == "" ]]; then + log "Required variable ${CHECK_KEY} is not set" + exit 1 + else + log "${CHECK_KEY}: ${CHECK_VALUE}" + fi +} +# checks that teleport binary can be found in path and runs 'teleport version' +check_teleport_binary() { + FOUND_TELEPORT_VERSION=$(${TELEPORT_BINARY_DIR}/teleport version) + if [[ "${FOUND_TELEPORT_VERSION}" == "" ]]; then + log "Cannot find Teleport binary" + return 1 + else + log "Found: ${FOUND_TELEPORT_VERSION}"; + return 0 + fi +} +# wrapper to download with curl +download() { + URL=$1 + OUTPUT_PATH=$2 + CURL_COMMAND="curl -fsSL --retry 5 --retry-delay 5" + # optionally allow disabling of TLS verification (can be useful on older distros + # which often have an out-of-date set of CA certificate bundle which won't validate) + if [[ ${DISABLE_TLS_VERIFICATION} == "true" ]]; then + CURL_COMMAND+=" -k" + fi + log "Running ${CURL_COMMAND} ${URL}" + log "Downloading to ${OUTPUT_PATH}" + # handle errors with curl + if ! ${CURL_COMMAND} -o "${OUTPUT_PATH}" "${URL}"; then + log_important "curl error downloading ${URL}" + log "On an older OS, this may be related to the CA certificate bundle being too old." + log "You can pass the hidden -k flag to this script to disable TLS verification - this is not recommended!" + exit 1 + fi + # check that the file has a non-zero size as an extra validation + check_exists_fatal wc xargs + FILE_SIZE="$(wc -c <"${OUTPUT_PATH}" | xargs)" + if [ "${FILE_SIZE}" -eq 0 ]; then + log_important "The downloaded file has a size of 0 bytes, which means an error occurred. Cannot continue." + exit 1 + else + log "Downloaded file size: ${FILE_SIZE} bytes" + fi + # if we have a hashing utility installed, also download and validate the checksum + SHA_COMMAND="" + # shasum is installed by default on macOS and some distros + if check_exists shasum; then + SHA_COMMAND="shasum -a 256" + # sha256sum is installed by default in some other distros + elif check_exists sha256sum; then + SHA_COMMAND="sha256sum" + fi + if [[ "${SHA_COMMAND}" != "" ]]; then + log "Will use ${SHA_COMMAND} to validate the checksum of the downloaded file" + SHA_URL="${URL}.sha256" + SHA_PATH="${OUTPUT_PATH}.sha256" + ${CURL_COMMAND} -o "${SHA_PATH}" "${SHA_URL}" + if ${SHA_COMMAND} --status -c "${SHA_PATH}"; then + log "The downloaded file's checksum validated correctly" + else + SHA_EXPECTED=$(cat "${SHA_PATH}") + SHA_ACTUAL=$(${SHA_COMMAND} "${OUTPUT_PATH}") + if check_exists awk; then + SHA_EXPECTED=$(echo "${SHA_EXPECTED}" | awk '{print $1}') + SHA_ACTUAL=$(echo "${SHA_ACTUAL}" | awk '{print $1}') + fi + log_important "Checksum of the downloaded file did not validate correctly" + log_important "Expected: ${SHA_EXPECTED}" + log_important "Got: ${SHA_ACTUAL}" + log_important "Try rerunning this script from the start. If the issue persists, contact Teleport support." + exit 1 + fi + else + log "shasum/sha256sum utilities not found, will skip checksum validation" + fi +} +# gets the filename from a full path (https://target.site/path/to/file.tar.gz -> file.tar.gz) +get_download_filename() { echo "${1##*/}"; } +# gets the pid of any running teleport process (and converts newlines to spaces) +get_teleport_pid() { + check_exists_fatal pgrep xargs + pgrep teleport | xargs echo +} +# returns a command which will start teleport using the config +get_teleport_start_command() { + echo "${TELEPORT_BINARY_DIR}/teleport start --config=${TELEPORT_CONFIG_PATH}" +} +# installs the teleport-provided launchd config +install_launchd_config() { + log "Installing Teleport launchd config to ${LAUNCHD_CONFIG_PATH}" + ${COPY_COMMAND} ./${TELEPORT_ARCHIVE_PATH}/examples/launchd/com.goteleport.teleport.plist ${LAUNCHD_CONFIG_PATH}/com.goteleport.teleport.plist +} +# installs the teleport-provided systemd unit +install_systemd_unit() { + log "Installing Teleport systemd unit to ${SYSTEMD_UNIT_PATH}" + ${COPY_COMMAND} ./${TELEPORT_ARCHIVE_PATH}/examples/systemd/teleport.service ${SYSTEMD_UNIT_PATH} + log "Reloading unit files (systemctl daemon-reload)" + systemctl daemon-reload +} +# formats the arguments as a yaml list +get_yaml_list() { + name="${1}" + list="${2}" + indentation="${3}" + echo "${indentation}${name}:" + for item in ${list}; do + echo "${indentation}- ${item}" + done +} + +# installs the provided teleport config (for app service) +install_teleport_app_config() { + log "Writing Teleport app service config to ${TELEPORT_CONFIG_PATH}" + CA_PINS_CONFIG=$(get_yaml_list "ca_pin" "${CA_PIN_HASHES}" " ") + cat << EOF > ${TELEPORT_CONFIG_PATH} +version: v3 +teleport: + nodename: ${NODENAME} + auth_token: ${JOIN_TOKEN} +${CA_PINS_CONFIG} + proxy_server: ${TARGET_HOSTNAME}:${TARGET_PORT} + log: + output: stderr + severity: INFO +auth_service: + enabled: no +ssh_service: + enabled: no +proxy_service: + enabled: no +app_service: + enabled: yes + apps: + - name: "${APP_NAME}" + uri: "${APP_URI}" + public_addr: ${APP_PUBLIC_ADDR} +EOF +} +# installs the provided teleport config (for database service) +install_teleport_database_config() { + log "Writing Teleport database service config to ${TELEPORT_CONFIG_PATH}" + CA_PINS_CONFIG=$(get_yaml_list "ca_pin" "${CA_PIN_HASHES}" " ") + + # This file is processed by `shellschek` as part of the lint step + # It detects an issue because of un-set variables - $index and $line. This check is called SC2154. + # However, that's not an issue, because those variables are replaced when we run go's text/template engine over it. + # When executing the script, those are no long variables but actual values. + # shellcheck disable=SC2154 + cat << EOF > ${TELEPORT_CONFIG_PATH} +version: v3 +teleport: + nodename: ${NODENAME} + auth_token: ${JOIN_TOKEN} +${CA_PINS_CONFIG} + proxy_server: ${TARGET_HOSTNAME}:${TARGET_PORT} + log: + output: stderr + severity: INFO +auth_service: + enabled: no +ssh_service: + enabled: no +proxy_service: + enabled: no +db_service: + enabled: "yes" + resources: + - labels: +EOF +} +# installs the provided teleport config (for node service) +install_teleport_node_config() { + log "Writing Teleport node service config to ${TELEPORT_CONFIG_PATH}" + ${TELEPORT_BINARY_DIR}/teleport node configure \ + --token ${JOIN_TOKEN} \ + ${JOIN_METHOD_FLAG} \ + --ca-pin ${CA_PINS} \ + --proxy ${TARGET_HOSTNAME}:${TARGET_PORT} \ + "${LABELS_FLAG[@]}" \ + --output ${TELEPORT_CONFIG_PATH} +} +# checks whether the given host is running macOS +is_macos_host() { if [[ ${OSTYPE} == "darwin"* ]]; then return 0; else return 1; fi } +# checks whether teleport is already running on the host +is_running_teleport() { + check_exists_fatal pgrep + TELEPORT_PID=$(get_teleport_pid) + if [[ "${TELEPORT_PID}" != "" ]]; then return 0; else return 1; fi +} +# checks whether the given host is running systemd as its init system +is_using_systemd() { if [ -d /run/systemd/system ]; then return 0; else return 1; fi } +# prints a warning if the host isn't running systemd +no_systemd_warning() { + log_important "This host is not running systemd, so Teleport cannot be started automatically when it exits." + log_important "Please investigate an alternative way to keep Teleport running." + log_important "You can find information in our documentation: ${TELEPORT_DOCS_URL}" + log_important "For now, Teleport will be started in the foreground - you can press Ctrl+C to exit." + log_only + log_only "Run this command to start Teleport in future:" + log_only "$(get_teleport_start_command)" + log_only + log_only "------------------------------------------------------------------------" + log_only "| IMPORTANT: TELEPORT WILL STOP RUNNING AFTER YOU CLOSE THIS TERMINAL! |" + log_only "| YOU MUST CONFIGURE A SERVICE MANAGER TO MAKE IT RUN ON STARTUP! |" + log_only "------------------------------------------------------------------------" + log_only +} +# print a message giving the name of the node and a link to the docs +# gives some debugging instructions if the service didn't start successfully +print_welcome_message() { + log_only "" + if is_running_teleport; then + log_only "Teleport has been started." + log_only "" + if is_using_systemd; then + log_only "View its status with 'sudo systemctl status teleport.service'" + log_only "View Teleport logs using 'sudo journalctl -u teleport.service'" + log_only "To stop Teleport, run 'sudo systemctl stop teleport.service'" + log_only "To start Teleport again if you stop it, run 'sudo systemctl start teleport.service'" + elif is_macos_host; then + log_only "View Teleport logs in '${MACOS_STDERR_LOG}' and '${MACOS_STDOUT_LOG}'" + log_only "To stop Teleport, run 'sudo launchctl unload ${LAUNCHD_CONFIG_PATH}/com.goteleport.teleport.plist'" + log_only "To start Teleport again if you stop it, run 'sudo launchctl load ${LAUNCHD_CONFIG_PATH}/com.goteleport.teleport.plist'" + fi + log_only "" + log_only "You can see this node connected in the Teleport web UI or 'tsh ls' with the name '${NODENAME}'" + log_only "Find more details on how to use Teleport here: https://goteleport.com/docs/user-manual/" + else + log_important "The Teleport service was installed, but it does not appear to have started successfully." + if is_using_systemd; then + log_important "Check the Teleport service's status with 'systemctl status teleport.service'" + log_important "View Teleport logs with 'journalctl -u teleport.service'" + elif is_macos_host; then + log_important "Check Teleport logs in '${MACOS_STDERR_LOG}' and '${MACOS_STDOUT_LOG}'" + fi + log_important "Contact Teleport support for further assistance." + fi + log_only "" +} +# start teleport in foreground (when there's no systemd) +start_teleport_foreground() { + log "Starting Teleport in the foreground" + # shellcheck disable=SC2091 + $(get_teleport_start_command) +} +# start teleport via launchd (after installing config) +start_teleport_launchd() { + log "Starting Teleport via launchctl. It will automatically be started whenever the system reboots." + launchctl load ${LAUNCHD_CONFIG_PATH}/com.goteleport.teleport.plist + sleep ${ALIVE_CHECK_DELAY} +} +# start teleport via systemd (after installing unit) +start_teleport_systemd() { + log "Starting Teleport via systemd. It will automatically be started whenever the system reboots." + systemctl enable teleport.service + systemctl start teleport.service + sleep ${ALIVE_CHECK_DELAY} +} +# checks whether teleport binaries exist on the host +teleport_binaries_exist() { + for BINARY_NAME in teleport tctl tsh; do + if [ -f ${TELEPORT_BINARY_DIR}/${BINARY_NAME} ]; then return 0; else return 1; fi + done +} +# checks whether a teleport config exists on the host +teleport_config_exists() { if [ -f ${TELEPORT_CONFIG_PATH} ]; then return 0; else return 1; fi; } +# checks whether a teleport data dir exists on the host +teleport_datadir_exists() { if [ -d ${TELEPORT_DATA_DIR} ]; then return 0; else return 1; fi; } + +# error out if any required values are not set +check_set TELEPORT_VERSION +check_set TARGET_HOSTNAME +check_set TARGET_PORT +check_set JOIN_TOKEN +check_set CA_PIN_HASHES +if [[ "${APP_INSTALL_MODE}" == "true" ]]; then + check_set APP_NAME + check_set APP_URI + check_set APP_PUBLIC_ADDR +fi + +### +# main script starts here +### +# check connectivity to teleport server/port +if [[ "${IGNORE_CONNECTIVITY_CHECK}" == "true" ]]; then + log "TELEPORT_IGNORE_CONNECTIVITY_CHECK=true, not running connectivity check" +else + log "Checking TCP connectivity to Teleport server (${TARGET_HOSTNAME}:${TARGET_PORT})" + if ! check_connectivity "${TARGET_HOSTNAME}" "${TARGET_PORT}"; then + # if we don't have a connectivity test method assigned, we know we couldn't run the test + if [[ ${CONNECTIVITY_TEST_METHOD} == "" ]]; then + log "Couldn't find nc, telnet or /dev/tcp to do a connection test" + log "Going to blindly continue without testing connectivity" + else + log_important "Couldn't open a connection to the Teleport server (${TARGET_HOSTNAME}:${TARGET_PORT}) via ${CONNECTIVITY_TEST_METHOD}" + log_important "This issue will need to be fixed before the script can continue." + log_important "If you think this is an error, add 'export TELEPORT_IGNORE_CONNECTIVITY_CHECK=true && ' before the curl command which runs the script." + exit 1 + fi + else + log "Connectivity to Teleport server (via ${CONNECTIVITY_TEST_METHOD}) looks good" + fi +fi + +# use OSTYPE variable to figure out host type/arch +if [[ "${OSTYPE}" == "linux"* ]]; then + # linux host, now detect arch + TELEPORT_BINARY_TYPE="linux" + ARCH=$(uname -m) + log "Detected host: ${OSTYPE}, using Teleport binary type ${TELEPORT_BINARY_TYPE}" + if [[ ${ARCH} == "armv7l" ]]; then + TELEPORT_ARCH="arm" + elif [[ ${ARCH} == "aarch64" ]]; then + TELEPORT_ARCH="arm64" + elif [[ ${ARCH} == "x86_64" ]]; then + TELEPORT_ARCH="amd64" + elif [[ ${ARCH} == "i686" ]]; then + TELEPORT_ARCH="386" + else + log_important "Error: cannot detect architecture from uname -m: ${ARCH}" + exit 1 + fi + log "Detected arch: ${ARCH}, using Teleport arch ${TELEPORT_ARCH}" + # if the download format is already set, we have no need to detect distro + if [[ ${TELEPORT_FORMAT} == "" ]]; then + # detect distro + # if /etc/os-release doesn't exist, we need to use some other logic + if [ ! -f /etc/os-release ]; then + if [ -f /etc/centos-release ]; then + if grep -q 'CentOS release 6' /etc/centos-release; then + log_important "Detected host type: CentOS 6 [$(cat /etc/centos-release)]" + log_important "Teleport will not work on CentOS 6 -based servers due to the glibc version being too low." + exit 1 + fi + elif [ -f /etc/redhat-release ]; then + if grep -q 'Red Hat Enterprise Linux Server release 5' /etc/redhat-release; then + log_important "Detected host type: RHEL5 [$(cat /etc/redhat-release)]" + log_important "Teleport will not work on RHEL5-based servers due to the glibc version being too low." + exit 1 + elif grep -q 'Red Hat Enterprise Linux Server release 6' /etc/redhat-release; then + log_important "Detected host type: RHEL6 [$(cat /etc/redhat-release)]" + log_important "Teleport will not work on RHEL6-based servers due to the glibc version being too low." + exit 1 + fi + fi + # use ID_LIKE value from /etc/os-release (if set) + # this is 'debian' on ubuntu/raspbian, 'centos rhel fedora' on amazon linux etc + else + check_exists_fatal cut + DISTRO_TYPE=$(grep ID_LIKE /etc/os-release | cut -d= -f2) || true + if [[ ${DISTRO_TYPE} == "" ]]; then + # use exact ID value from /etc/os-release if ID_LIKE is not set + DISTRO_TYPE=$(grep -w ID /etc/os-release | cut -d= -f2) + fi + if [[ ${DISTRO_TYPE} =~ "debian" ]]; then + TELEPORT_FORMAT="deb" + elif [[ "$DISTRO_TYPE" =~ "amzn"* ]] || [[ ${DISTRO_TYPE} =~ "centos"* ]] || [[ ${DISTRO_TYPE} =~ "rhel" ]] || [[ ${DISTRO_TYPE} =~ "fedora"* ]] || \ + [[ ${DISTRO_TYPE} == *"suse"* ]] || [[ ${DISTRO_TYPE} =~ "sles"* ]]; then + TELEPORT_FORMAT="rpm" + else + log "Couldn't match a distro type using /etc/os-release, falling back to tarball installer" + TELEPORT_FORMAT="tarball" + fi + fi + log "Detected distro type: ${DISTRO_TYPE}" + #suse, also identified as sles, uses a different path for its systemd then other distro types like ubuntu + if [[ ${DISTRO_TYPE} =~ "suse"* ]] || [[ ${DISTRO_TYPE} =~ "sles"* ]]; then + SYSTEMD_UNIT_PATH="/etc/systemd/system/teleport.service" + fi + fi +elif [[ "${OSTYPE}" == "darwin"* ]]; then + # macOS host, now detect arch + TELEPORT_BINARY_TYPE="darwin" + ARCH=$(uname -m) + log "Detected host: ${OSTYPE}, using Teleport binary type ${TELEPORT_BINARY_TYPE}" + if [[ ${ARCH} == "arm64" ]]; then + TELEPORT_ARCH="arm64" + elif [[ ${ARCH} == "x86_64" ]]; then + TELEPORT_ARCH="amd64" + else + log_important "Error: unsupported architecture from uname -m: ${ARCH}" + exit 1 + fi + log "Detected macOS ${ARCH} architecture, using Teleport arch ${TELEPORT_ARCH}" + TELEPORT_FORMAT="tarball" +else + log_important "Error - unsupported platform: ${OSTYPE}" + exit 1 +fi +log "Using Teleport distribution: ${TELEPORT_FORMAT}" + +# create temporary directory and exit cleanup logic +TEMP_DIR=$(mktemp -d -t teleport-XXXXXXXXXX) +log "Created temp dir ${TEMP_DIR}" +pushd "${TEMP_DIR}" >/dev/null 2>&1 + +finish() { + popd >/dev/null 2>&1 + rm -rf "${TEMP_DIR}" +} +trap finish EXIT + +# optional format override (mostly for testing) +if [[ ${OVERRIDE_FORMAT} != "" ]]; then + TELEPORT_FORMAT="${OVERRIDE_FORMAT}" + log "Overriding TELEPORT_FORMAT to ${OVERRIDE_FORMAT}" +fi + +# check whether teleport is running already +# if it is, we exit gracefully with an error +if is_running_teleport; then + if [[ ${IGNORE_CHECKS} != "true" ]]; then + TELEPORT_PID=$(get_teleport_pid) + log_header "Warning: Teleport appears to already be running on this host (pid: ${TELEPORT_PID})" + log_cleanup_message + exit 1 + else + log "Ignoring is_running_teleport as requested" + fi +fi + +# check for existing config file +if teleport_config_exists; then + if [[ ${IGNORE_CHECKS} != "true" ]]; then + log_header "Warning: There is already a Teleport config file present at ${TELEPORT_CONFIG_PATH}." + log_cleanup_message + exit 1 + else + log "Ignoring teleport_config_exists as requested" + fi +fi + +# check for existing data directory +if teleport_datadir_exists; then + if [[ ${IGNORE_CHECKS} != "true" ]]; then + log_header "Warning: Found existing Teleport data directory (${TELEPORT_DATA_DIR})." + log_cleanup_message + exit 1 + else + log "Ignoring teleport_datadir_exists as requested" + fi +fi + +# check for existing binaries +if teleport_binaries_exist; then + if [[ ${IGNORE_CHECKS} != "true" ]]; then + log_header "Warning: Found existing Teleport binaries under ${TELEPORT_BINARY_DIR}." + log_cleanup_message + exit 1 + else + log "Ignoring teleport_binaries_exist as requested" + fi +fi + +install_from_file() { + # select correct URL/installation method based on distro + if [[ ${TELEPORT_FORMAT} == "tarball" ]]; then + URL="https://cdn.teleport.dev/${TELEPORT_PACKAGE_NAME}-v${TELEPORT_VERSION}-${TELEPORT_BINARY_TYPE}-${TELEPORT_ARCH}-bin.tar.gz" + + # check that needed tools are installed + check_exists_fatal curl tar + # download tarball + log "Downloading Teleport ${TELEPORT_FORMAT} release ${TELEPORT_VERSION}" + DOWNLOAD_FILENAME=$(get_download_filename "${URL}") + download "${URL}" "${TEMP_DIR}/${DOWNLOAD_FILENAME}" + # extract tarball + tar -xzf "${TEMP_DIR}/${DOWNLOAD_FILENAME}" -C "${TEMP_DIR}" + # install binaries to /usr/local/bin + for BINARY in ${TELEPORT_BINARY_LIST}; do + ${COPY_COMMAND} "${TELEPORT_ARCHIVE_PATH}/${BINARY}" "${TELEPORT_BINARY_DIR}/" + done + elif [[ ${TELEPORT_FORMAT} == "deb" ]]; then + # convert teleport arch to deb arch + if [[ ${TELEPORT_ARCH} == "amd64" ]]; then + DEB_ARCH="amd64" + elif [[ ${TELEPORT_ARCH} == "386" ]]; then + DEB_ARCH="i386" + elif [[ ${TELEPORT_ARCH} == "arm" ]]; then + DEB_ARCH="arm" + elif [[ ${TELEPORT_ARCH} == "arm64" ]]; then + DEB_ARCH="arm64" + fi + URL="https://cdn.teleport.dev/${TELEPORT_PACKAGE_NAME}_${TELEPORT_VERSION}_${DEB_ARCH}.deb" + check_deb_not_already_installed + # check that needed tools are installed + check_exists_fatal curl dpkg + # download deb and register cleanup operation + log "Downloading Teleport ${TELEPORT_FORMAT} release ${TELEPORT_VERSION}" + DOWNLOAD_FILENAME=$(get_download_filename "${URL}") + download "${URL}" "${TEMP_DIR}/${DOWNLOAD_FILENAME}" + # install deb + log "Using dpkg to install ${TEMP_DIR}/${DOWNLOAD_FILENAME}" + dpkg -i "${TEMP_DIR}/${DOWNLOAD_FILENAME}" + elif [[ ${TELEPORT_FORMAT} == "rpm" ]]; then + # convert teleport arch to rpm arch + if [[ ${TELEPORT_ARCH} == "amd64" ]]; then + RPM_ARCH="x86_64" + elif [[ ${TELEPORT_ARCH} == "386" ]]; then + RPM_ARCH="i386" + elif [[ ${TELEPORT_ARCH} == "arm" ]]; then + RPM_ARCH="arm" + elif [[ ${TELEPORT_ARCH} == "arm64" ]]; then + RPM_ARCH="arm64" + fi + URL="https://cdn.teleport.dev/${TELEPORT_PACKAGE_NAME}-${TELEPORT_VERSION}-1.${RPM_ARCH}.rpm" + check_rpm_not_already_installed + # check for package managers + if check_exists dnf; then + log "Found 'dnf' package manager, using it" + PACKAGE_MANAGER_COMMAND="dnf -y install" + elif check_exists yum; then + log "Found 'yum' package manager, using it" + PACKAGE_MANAGER_COMMAND="yum -y localinstall" + elif check_exists zypper; then + log "Found 'zypper' package manager, using it" + PACKAGE_MANAGER_COMMAND="zypper --non-interactive install" + else + PACKAGE_MANAGER_COMMAND="" + log "Cannot find 'yum' or 'dnf' package manager commands, will try installing the rpm manually instead" + fi + # check that needed tools are installed + check_exists_fatal curl + log "Downloading Teleport ${TELEPORT_FORMAT} release ${TELEPORT_VERSION}" + DOWNLOAD_FILENAME=$(get_download_filename "${URL}") + download "${URL}" "${TEMP_DIR}/${DOWNLOAD_FILENAME}" + # install with package manager if available + if [[ ${PACKAGE_MANAGER_COMMAND} != "" ]]; then + log "Installing Teleport release from ${TEMP_DIR}/${DOWNLOAD_FILENAME} using ${PACKAGE_MANAGER_COMMAND}" + # install rpm with package manager + ${PACKAGE_MANAGER_COMMAND} "${TEMP_DIR}/${DOWNLOAD_FILENAME}" + # use rpm if we couldn't find a package manager + else + # install RPM (in upgrade mode) + log "Using rpm to install ${TEMP_DIR}/${DOWNLOAD_FILENAME}" + rpm -Uvh "${TEMP_DIR}/${DOWNLOAD_FILENAME}" + fi + else + log_important "Can't figure out what Teleport format to use" + exit 1 + fi +} + +install_from_repo() { + if [[ "${REPO_CHANNEL}" == "" ]]; then + # By default, use the current version's channel. + REPO_CHANNEL=stable/v"${TELEPORT_VERSION//.*/}" + fi + + # Populate $ID, $VERSION_ID, $VERSION_CODENAME and other env vars identifying the OS. + # shellcheck disable=SC1091 + . /etc/os-release + + PACKAGE_LIST=$(package_list) + if [ "$ID" == "debian" ] || [ "$ID" == "ubuntu" ]; then + # old versions of ubuntu require that keys get added by `apt-key add`, without + # adding the key apt shows a key signing error when installing teleport. + if [[ + ($ID == "ubuntu" && $VERSION_ID == "16.04") || \ + ($ID == "debian" && $VERSION_ID == "9" ) + ]]; then + apt install apt-transport-https gnupg -y + curl -fsSL https://apt.releases.teleport.dev/gpg | apt-key add - + echo "deb https://apt.releases.teleport.dev/${ID} ${VERSION_CODENAME} ${REPO_CHANNEL}" > /etc/apt/sources.list.d/teleport.list + else + curl -fsSL https://apt.releases.teleport.dev/gpg \ + -o /usr/share/keyrings/teleport-archive-keyring.asc + echo "deb [signed-by=/usr/share/keyrings/teleport-archive-keyring.asc] \ + https://apt.releases.teleport.dev/${ID} ${VERSION_CODENAME} ${REPO_CHANNEL}" > /etc/apt/sources.list.d/teleport.list + fi + apt-get update + apt-get install -y ${PACKAGE_LIST} + elif [ "$ID" = "amzn" ] || [ "$ID" = "rhel" ] || [ "$ID" = "centos" ]; then + if [ "$ID" = "rhel" ]; then + VERSION_ID="${VERSION_ID//.*/}" # convert version numbers like '7.2' to only include the major version + fi + yum install -y yum-utils + yum-config-manager --add-repo \ + "$(rpm --eval "https://yum.releases.teleport.dev/$ID/$VERSION_ID/Teleport/%{_arch}/${REPO_CHANNEL}/teleport.repo")" + + # Remove metadata cache to prevent cache from other channel (eg, prior version) + # See: https://github.com/gravitational/teleport/issues/22581 + yum --disablerepo="*" --enablerepo="teleport" clean metadata + + yum install -y ${PACKAGE_LIST} + elif [ "$ID" = "sles" ] || [ "$ID" = "opensuse-tumbleweed" ] || [ "$ID" = "opensuse-leap" ]; then + if [ "$ID" = "opensuse-tumbleweed" ]; then + VERSION_ID="15" # tumbleweed uses dated VERSION_IDs like 20230702 + else + VERSION_ID="${VERSION_ID//.*/}" # convert version numbers like '7.2' to only include the major version + fi + sudo rpm --import "https://zypper.releases.teleport.dev/gpg" + sudo zypper --non-interactive addrepo "$(rpm --eval "https://zypper.releases.teleport.dev/sles/$VERSION_ID/Teleport/%{_arch}/${REPO_CHANNEL}/teleport.repo")" + sudo zypper --gpg-auto-import-keys refresh + sudo zypper --non-interactive install ${PACKAGE_LIST} + else + echo "Unsupported distro: $ID" + exit 1 + fi +} + +# package_list returns the list of packages to install. +# The list of packages can be fed into yum or apt because they already have the expected format when pinning versions. +package_list() { + TELEPORT_PACKAGE_PIN_VERSION=${TELEPORT_PACKAGE_NAME} + TELEPORT_UPDATER_PIN_VERSION="${TELEPORT_PACKAGE_NAME}-updater" + + if [[ "${TELEPORT_FORMAT}" == "deb" ]]; then + TELEPORT_PACKAGE_PIN_VERSION+="=${TELEPORT_VERSION}" + TELEPORT_UPDATER_PIN_VERSION+="=${TELEPORT_VERSION}" + + elif [[ "${TELEPORT_FORMAT}" == "rpm" ]]; then + TELEPORT_YUM_VERSION="${TELEPORT_VERSION//-/_}" + TELEPORT_PACKAGE_PIN_VERSION+="-${TELEPORT_YUM_VERSION}" + TELEPORT_UPDATER_PIN_VERSION+="-${TELEPORT_YUM_VERSION}" + fi + + PACKAGE_LIST=${TELEPORT_PACKAGE_PIN_VERSION} + # (warning): This expression is constant. Did you forget the $ on a variable? + # Disabling the warning above because expression is templated. + # shellcheck disable=SC2050 + if is_using_systemd && [[ "false" == "true" ]]; then + # Teleport Updater requires systemd. + PACKAGE_LIST+=" ${TELEPORT_UPDATER_PIN_VERSION}" + fi + echo ${PACKAGE_LIST} +} + +is_repo_available() { + if [[ "${OSTYPE}" != "linux"* ]]; then + return 1 + fi + + # Populate $ID, $VERSION_ID and other env vars identifying the OS. + # shellcheck disable=SC1091 + . /etc/os-release + + # The following distros+version have a Teleport repository to install from. + case "${ID}-${VERSION_ID}" in + ubuntu-16.04* | ubuntu-18.04* | ubuntu-20.04* | ubuntu-22.04* | \ + debian-9* | debian-10* | debian-11* | debian-12* | \ + rhel-7* | rhel-8* | rhel-9* | \ + centos-7* | centos-8* | centos-9* | \ + amzn-2 | amzn-2023 | \ + opensuse-tumbleweed* | sles-12* | sles-15* | opensuse-leap-15*) + return 0;; + esac + + return 1 +} + +if is_repo_available; then + log "Installing repo for distro $ID." + install_from_repo +else + log "Installing from binary file." + install_from_file +fi + +# check that teleport binary can be found and runs +if ! check_teleport_binary; then + log_important "The Teleport binary could not be found at ${TELEPORT_BINARY_DIR} as expected." + log_important "This usually means that there was an error during installation." + log_important "Check this log for obvious signs of error and contact Teleport support" + log_important "for further assistance." + exit 1 +fi + +# install teleport config +# check the mode and write the appropriate config type +if [[ "${APP_INSTALL_MODE}" == "true" ]]; then + install_teleport_app_config +elif [[ "${DB_INSTALL_MODE}" == "true" ]]; then + install_teleport_database_config +else + install_teleport_node_config +fi + + +# Used to track whether a Teleport agent was installed using this method. +export TELEPORT_INSTALL_METHOD_NODE_SCRIPT="true" + +# install systemd unit if applicable (linux hosts) +if is_using_systemd; then + log "Host is using systemd" + # we only need to manually install the systemd config if teleport was installed via tarball + # all other packages will deploy it automatically + if [[ ${TELEPORT_FORMAT} == "tarball" ]]; then + install_systemd_unit + fi + start_teleport_systemd + print_welcome_message +# install launchd config on macOS hosts +elif is_macos_host; then + log "Host is running macOS" + install_launchd_config + start_teleport_launchd + print_welcome_message +# not a macOS host and no systemd available, print a warning +# and temporarily start Teleport in the foreground +else + log "Host does not appear to be using systemd" + no_systemd_warning + start_teleport_foreground +fi diff --git a/terminal/setup.sh b/terminal/setup.sh index f7b540bf..15f4663c 100755 --- a/terminal/setup.sh +++ b/terminal/setup.sh @@ -75,11 +75,26 @@ overwrite_starship_toml() { fi } +overwrite_starship_vanilla_toml() { + if [ -f "./terminal/starship-vanilla.toml" ]; then + echo "Overwriting starship.toml..." + cp ./terminal/starship-vanilla.toml ~/.config/starship.toml + else + echo "starship_vanilla.toml not found in the repository." + exit 1 + fi + +} + # Main script execution install_fish set_default_fish install_starship configure_starship -overwrite_starship_toml +if [ -z "$XDG_CURRENT_DESKTOP" ]; then + overwrite_starship_toml +else + overwrite_starship_vanilla_toml +fi echo "Fish shell and Starship prompt setup complete!" diff --git a/terminal/starship-vanilla.toml b/terminal/starship-vanilla.toml new file mode 100644 index 00000000..2b843fc2 --- /dev/null +++ b/terminal/starship-vanilla.toml @@ -0,0 +1,167 @@ +"$schema" = 'https://starship.rs/config-schema.json' + +format = """ +[](color_orange)\ +$os\ +$username\ +[](bg:color_yellow fg:color_orange)\ +$directory\ +[](fg:color_yellow bg:color_aqua)\ +$git_branch\ +$git_status\ +[](fg:color_aqua bg:color_blue)\ +$c\ +$rust\ +$golang\ +$nodejs\ +$php\ +$java\ +$kotlin\ +$haskell\ +$python\ +[](fg:color_blue bg:color_bg3)\ +$docker_context\ +$conda\ +[](fg:color_bg3 bg:color_bg1)\ +$time\ +[ ](fg:color_bg1)\ +$line_break$character""" + +palette = 'gruvbox_dark' + +[palettes.gruvbox_dark] +color_fg0 = '#fbf1c7' +color_bg1 = '#3c3836' +color_bg3 = '#665c54' +color_blue = '#458588' +color_aqua = '#689d6a' +color_green = '#98971a' +color_orange = '#d65d0e' +color_purple = '#b16286' +color_red = '#cc241d' +color_yellow = '#d79921' + +[os] +disabled = false +style = "bg:color_orange fg:color_fg0" + +[os.symbols] +Windows = "󰍲" +Ubuntu = "󰕈" +SUSE = "" +Raspbian = "󰐿" +Mint = "󰣭" +Macos = "󰀵" +Manjaro = "" +Linux = "󰌽" +Gentoo = "󰣨" +Fedora = "󰣛" +Alpine = "" +Amazon = "" +Android = "" +Arch = "󰣇" +Artix = "󰣇" +CentOS = "" +Debian = "󰣚" +Redhat = "󱄛" +RedHatEnterprise = "󱄛" + +[username] +show_always = true +style_user = "bg:color_orange fg:color_fg0" +style_root = "bg:color_orange fg:color_fg0" +format = '[ $user ]($style)' + +[directory] +style = "fg:color_fg0 bg:color_yellow" +format = "[ $path ]($style)" +truncation_length = 3 +truncation_symbol = "…/" + +[directory.substitutions] +"Documents" = "󰈙 " +"Downloads" = " " +"Music" = "󰝚 " +"Pictures" = " " +"Developer" = "󰲋 " + +[git_branch] +symbol = "" +style = "bg:color_aqua" +format = '[[ $symbol $branch ](fg:color_fg0 bg:color_aqua)]($style)' + +[git_status] +style = "bg:color_aqua" +format = '[[($all_status$ahead_behind )](fg:color_fg0 bg:color_aqua)]($style)' + +[nodejs] +symbol = "" +style = "bg:color_blue" +format = '[[ $symbol( $version) ](fg:color_fg0 bg:color_blue)]($style)' + +[c] +symbol = " " +style = "bg:color_blue" +format = '[[ $symbol( $version) ](fg:color_fg0 bg:color_blue)]($style)' + +[rust] +symbol = "" +style = "bg:color_blue" +format = '[[ $symbol( $version) ](fg:color_fg0 bg:color_blue)]($style)' + +[golang] +symbol = "" +style = "bg:color_blue" +format = '[[ $symbol( $version) ](fg:color_fg0 bg:color_blue)]($style)' + +[php] +symbol = "" +style = "bg:color_blue" +format = '[[ $symbol( $version) ](fg:color_fg0 bg:color_blue)]($style)' + +[java] +symbol = " " +style = "bg:color_blue" +format = '[[ $symbol( $version) ](fg:color_fg0 bg:color_blue)]($style)' + +[kotlin] +symbol = "" +style = "bg:color_blue" +format = '[[ $symbol( $version) ](fg:color_fg0 bg:color_blue)]($style)' + +[haskell] +symbol = "" +style = "bg:color_blue" +format = '[[ $symbol( $version) ](fg:color_fg0 bg:color_blue)]($style)' + +[python] +symbol = "" +style = "bg:color_blue" +format = '[[ $symbol( $version) ](fg:color_fg0 bg:color_blue)]($style)' + +[docker_context] +symbol = "" +style = "bg:color_bg3" +format = '[[ $symbol( $context) ](fg:#83a598 bg:color_bg3)]($style)' + +[conda] +style = "bg:color_bg3" +format = '[[ $symbol( $environment) ](fg:#83a598 bg:color_bg3)]($style)' + +[time] +disabled = false +time_format = "%R" +style = "bg:color_bg1" +format = '[[  $time ](fg:color_fg0 bg:color_bg1)]($style)' + +[line_break] +disabled = false + +[character] +disabled = false +success_symbol = '[](bold fg:color_green)' +error_symbol = '[](bold fg:color_red)' +vimcmd_symbol = '[](bold fg:color_green)' +vimcmd_replace_one_symbol = '[](bold fg:color_purple)' +vimcmd_replace_symbol = '[](bold fg:color_purple)' +vimcmd_visual_symbol = '[](bold fg:color_yellow)' \ No newline at end of file