#!/bin/bash
if [[ "${DEBUG:-false}" == "true" ]]; then
    set -x
fi

## Test functions
# Returns true in test mode and false otherwise
is_test() { [[ "${TELEPORT_TEST_MODE}" == "true" ]]; }
# Check for FIPS
# In regular mode, we do this by looking at the ExecStart command for teleport.service to see whether it
# contains 'fips' or not (which is set during packer build). We use this to modify the auth service's
# configuration depending on whether FIPS is in use or not.
# In test mode, it uses the value of the TELEPORT_TEST_FIPS_MODE variable.
is_fips() {
    if is_test; then
        if [[ "${TELEPORT_TEST_FIPS_MODE}" == "true" ]]; then return 0; else return 1; fi
    else
        grep "ExecStart" /etc/systemd/system/teleport.service | grep -q "fips"
    fi
}
# systemctl wrapper which just echoes commands in test mode rather than running them
systemctl_wrap() { if is_test; then echo "$@"; else systemctl "$@"; fi }

# Allow the config paths to be overridden for testing
USE_CONFIG_PATH="${TELEPORT_CONFIG_PATH:-/etc/teleport.yaml}"
USE_CONFD_DIR="${TELEPORT_CONFD_DIR:-/etc/teleport.d}"
# If a copy of the Teleport config file already exists, don't change it
# and just exit this script with success.
if [ -f ${USE_CONFIG_PATH} ]; then
    # Optionally allow this behaviour to be suppressed for tests.
    if ! is_test; then
        echo "${USE_CONFIG_PATH} already exists. Exiting with success."
        exit 0
    fi
fi

# Source variables from user-data file
# shellcheck disable=SC1090
source "${USE_CONFD_DIR}/conf"

## Helper functions
aws_metadata_get() {
    # request path is the part after /latest/ i.e. meta-data/local-ipv4
    REQUEST="$1"
    CURL_EXTRA_ARGS=""
    if [[ "$2" != "" ]]; then
        CURL_EXTRA_ARGS="$2"
    fi
    case $REQUEST in
        LOCAL_IP)
            REQUEST_PATH="meta-data/local-ipv4"
            ;;
        LOCAL_HOSTNAME)
            REQUEST_PATH="meta-data/local-hostname"
            ;;
        PUBLIC_IP)
            REQUEST_PATH="meta-data/public-ipv4"
            ;;
        *)
            echo "No request path defined for ${REQUEST}"
            exit 126
            ;;
    esac

    if ! is_test; then
        IMDS_TOKEN=$(curl -m5 -sS -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 300")
        IMDS_TOKEN_HEADER="X-aws-ec2-metadata-token: ${IMDS_TOKEN}"
        curl -m5 -sS -H "${IMDS_TOKEN_HEADER}" ${CURL_EXTRA_ARGS} http://169.254.169.254/latest/${REQUEST_PATH}
    else
        # return a pre-calculated value
        VARIABLE="TELEPORT_TESTVAR_${REQUEST}"
        echo "${!VARIABLE}"
    fi
}

write_https_keypairs_section() {
  cat << EOS >> ${USE_CONFIG_PATH}
  https_keypairs:
    - cert_file: /var/lib/teleport/fullchain.pem
      key_file: /var/lib/teleport/privkey.pem
EOS
}

write_database_section() {
    # use the value of the variable name which was passed in
    # this is done because there are two different ways of getting the external hostname
    # depending on how the AMI is being used and we should support both
    EXTERNAL_HOSTNAME="${!1}"
    if [[ "${USE_ACM}" == "true" && "${TELEPORT_PROXY_SERVER_LB}" != "" ]]; then
        # if an alias for the ACM NLB is configured, use that for proxy's database public_addrs
        if [[ "${TELEPORT_PROXY_SERVER_NLB_ALIAS}" != "" ]]; then
            MYSQL_PUBLIC_ADDR="${TELEPORT_PROXY_SERVER_NLB_ALIAS}:3036"
            POSTGRES_PUBLIC_ADDR="${TELEPORT_PROXY_SERVER_NLB_ALIAS}:5432"
            MONGO_PUBLIC_ADDR="${TELEPORT_PROXY_SERVER_NLB_ALIAS}:27017"
        else
            MYSQL_PUBLIC_ADDR="${TELEPORT_PROXY_SERVER_LB}:3036"
            POSTGRES_PUBLIC_ADDR="${TELEPORT_PROXY_SERVER_LB}:5432"
            MONGO_PUBLIC_ADDR="${TELEPORT_PROXY_SERVER_LB}:27017"
        fi
    else
        MYSQL_PUBLIC_ADDR="${EXTERNAL_HOSTNAME:-$PUBLIC_IP}:3036"
        POSTGRES_PUBLIC_ADDR="${EXTERNAL_HOSTNAME:-$PUBLIC_IP}:5432"
        MONGO_PUBLIC_ADDR="${EXTERNAL_HOSTNAME:-$PUBLIC_IP}:27017"
    fi
   # set up the database listeners and public addresses
    if [[ "${TELEPORT_ENABLE_MYSQL}" == "true" ]]; then
    cat << EOS >> ${USE_CONFIG_PATH}
  mysql_listen_addr: 0.0.0.0:3036
  mysql_public_addr: ${MYSQL_PUBLIC_ADDR}
EOS
   fi
   # Add the postgres listener only if the POSTGRES_HOST is blank
   if [[ "${TELEPORT_ENABLE_POSTGRES}" == "true" && "${POSTGRES_HOSTNAME}" == "" ]]; then
    cat << EOS >> ${USE_CONFIG_PATH}
  postgres_listen_addr: 0.0.0.0:5432
  postgres_public_addr: ${POSTGRES_PUBLIC_ADDR}
EOS
   fi
   if [[ "${TELEPORT_ENABLE_MONGODB}" == "true" ]]; then
    cat << EOS >> ${USE_CONFIG_PATH}
  mongo_listen_addr: 0.0.0.0:27017
  mongo_public_addr: ${MONGO_PUBLIC_ADDR}
EOS
   fi
}

write_kubernetes_section() {
    # use the value of the variable name which was passed in
    # this is done because there are two different ways of getting the external hostname
    # depending on how the AMI is being used and we should support both
    EXTERNAL_HOSTNAME="${!1}"
    if [[ "${USE_ACM}" == "true" && "${TELEPORT_PROXY_SERVER_LB}" != "" ]]; then
        # if an alias for the ACM NLB is configured, use that for proxy's kubernetes public_addr
        if [[ "${TELEPORT_PROXY_SERVER_NLB_ALIAS}" != "" ]]; then
            KUBERNETES_PUBLIC_ADDR="${TELEPORT_PROXY_SERVER_NLB_ALIAS}:3026"
        else
            KUBERNETES_PUBLIC_ADDR="${TELEPORT_PROXY_SERVER_LB}:3026"
        fi
    else
        KUBERNETES_PUBLIC_ADDR="${EXTERNAL_HOSTNAME:-$PUBLIC_IP}:3026"
    fi
    # set up the kubernetes listener
    cat << EOS >> ${USE_CONFIG_PATH}
  kube_listen_addr: 0.0.0.0:3026
  kube_public_addr: ${KUBERNETES_PUBLIC_ADDR}
EOS
}

write_ssh_and_tunnel_section() {
    TUNNEL_PORT=3024
    SSH_PORT=3023
    POSTGRES_PORT=443
    if [[ "${TELEPORT_PROXY_SERVER_LB}" != "" ]]; then
        # ACM
        if [[ "${USE_ACM}" == "true" ]]; then
            SSH_HOSTNAME="${TELEPORT_PROXY_SERVER_LB}"
            TUNNEL_HOSTNAME="${TELEPORT_PROXY_SERVER_LB}"

            if [[ "${TELEPORT_PROXY_SERVER_NLB_ALIAS}" != "" ]]; then
                # if an alias for the ACM NLB is configured, use that for the proxy's ssh_public_addr
                SSH_HOSTNAME="${TELEPORT_PROXY_SERVER_NLB_ALIAS}"
                # if an alias for the ACM NLB is configured, use that for the proxy's tunnel_public_addr
                TUNNEL_HOSTNAME="${TELEPORT_PROXY_SERVER_NLB_ALIAS}"
                # when using ACM and not using TLS routing, we have to add a specific public addr for postgres
                # which lives on the NLB (rather than the ACM ALB) so that Teleport's multiplexer can be used to
                # route connections
                # if an alias for the ACM NLB is configured, use that for the proxy's postgres_public_addr
                POSTGRES_HOSTNAME="${TELEPORT_PROXY_SERVER_NLB_ALIAS}"
            fi
        # non-ACM
        else
            TUNNEL_HOSTNAME="${TELEPORT_EXTERNAL_HOSTNAME:-$PUBLIC_IP}"
            SSH_HOSTNAME="${TELEPORT_EXTERNAL_HOSTNAME:-$PUBLIC_IP}"
            if [[ "${TELEPORT_DOMAIN_NAME}" != "" ]]; then
                # check whether an external domain name is configured (HA mode)
                # if not (starter mode), use an external hostname if configured, and fall back to public IP
                SSH_HOSTNAME="${TELEPORT_DOMAIN_NAME}"
                # Teleport used to only support multiplexing the web and tunnel listeners on the same port
                # With the advent of TLS routing, this is no longer a thing so we always use port 3024
                # when TLS routing is disabled
                TUNNEL_HOSTNAME="${TELEPORT_DOMAIN_NAME}"
            fi
        fi
    fi
    # if postgres hostname is set, add it to the config
    # if not, don't add it and use teleport's default
    if [[ "${POSTGRES_HOSTNAME}" != "" ]]; then
      echo "  postgres_public_addr: ${POSTGRES_HOSTNAME}:${POSTGRES_PORT}" >> ${USE_CONFIG_PATH}
    fi
    cat << EOS >> ${USE_CONFIG_PATH}
  ssh_public_addr: ${SSH_HOSTNAME}:${SSH_PORT}
  tunnel_public_addr: ${TUNNEL_HOSTNAME}:${TUNNEL_PORT}
EOS
}

## Start
# Set up AWS variables
LOCAL_IP=$(aws_metadata_get LOCAL_IP)
LOCAL_HOSTNAME=$(aws_metadata_get LOCAL_HOSTNAME)
LOCAL_HOSTNAME=${LOCAL_HOSTNAME//./-}
if aws_metadata_get PUBLIC_IP -i | grep -q 404; then
    PUBLIC_IP=${LOCAL_IP}
else
    PUBLIC_IP=$(aws_metadata_get PUBLIC_IP)
fi

# If the teleport user and adm group exist, create /var/lib/teleport if it doesn't exist
# and fix permissions appropriately
if getent passwd teleport >/dev/null 2>&1 && getent group adm >/dev/null 2>&1; then
    if [ ! -d /var/lib/teleport ]; then
        mkdir -p /var/lib/teleport
    fi
    chown -R teleport:adm /var/lib/teleport
fi
touch ${USE_CONFIG_PATH}
chmod 664 ${USE_CONFIG_PATH}

# Use Let's Encrypt by default unless we are explicitly using ACM here
if [[ "${USE_ACM}" != "true" ]]; then
    rm -f ${USE_CONFD_DIR}/role.all-acm
    echo "use-letsencrypt" > ${USE_CONFD_DIR}/use-letsencrypt
fi

# Determine whether this is a FIPS AMI or not
# With FIPS: auth_service.authentication.local_auth must be 'false' or Teleport will not start
# Be careful with the indentation here
AUTHENTICATION_STANZA="second_factor: on
    webauthn:
      rp_id: $TELEPORT_DOMAIN_NAME"
if is_fips; then
    AUTHENTICATION_STANZA="local_auth: false
    ${AUTHENTICATION_STANZA}"
fi
# If auth type is set in the Terraform variables, add this to the config on the auth servers.
if [[ "${TELEPORT_AUTH_TYPE}" != "" ]]; then
  AUTHENTICATION_STANZA="type: ${TELEPORT_AUTH_TYPE}
    ${AUTHENTICATION_STANZA}"
fi

if [[ "${TELEPORT_ROLE}" == "auth" ]]; then
    echo "auth" > ${USE_CONFD_DIR}/role.auth
    # Teleport Auth server is using DynamoDB as a backend
    # On AWS, see dynamodb.tf for details
    cat >${USE_CONFIG_PATH} <<EOF
teleport:
  nodename: ${LOCAL_HOSTNAME}
  advertise_ip: ${LOCAL_IP}
  log:
    output: stderr
    severity: INFO
  data_dir: /var/lib/teleport
  storage:
    type: dynamodb
    region: ${EC2_REGION}
    table_name: ${TELEPORT_DYNAMO_TABLE_NAME}
    audit_events_uri: dynamodb://${TELEPORT_DYNAMO_EVENTS_TABLE_NAME}
    audit_sessions_uri: s3://${TELEPORT_S3_BUCKET}/records

ssh_service:
  enabled: no

proxy_service:
  enabled: no

auth_service:
  enabled: yes
  proxy_protocol: on
  public_addr: ${TELEPORT_AUTH_SERVER_LB}:3025
  keep_alive_interval: 1m
  keep_alive_count_max: 3
  listen_addr: 0.0.0.0:3025
  authentication:
    ${AUTHENTICATION_STANZA}
  cluster_name: ${TELEPORT_CLUSTER_NAME}
EOF

    # copy and set up license if provided
    if [[ "${TELEPORT_LICENSE_PATH}" != "" ]]; then
        aws ssm get-parameter --with-decryption --name /teleport/${TELEPORT_CLUSTER_NAME}/license --region ${EC2_REGION} --query 'Parameter.Value' --output text > /var/lib/teleport/license.pem
        chown teleport:adm /var/lib/teleport/license.pem
        echo "  license_file: /var/lib/teleport/license.pem" >> ${USE_CONFIG_PATH}
    fi

    # configure proxy_listener_mode
    if [[ "${USE_TLS_ROUTING}" == "true" ]]; then
        echo "  proxy_listener_mode: multiplex" >> ${USE_CONFIG_PATH}
    # support legacy untested method introduced in https://github.com/gravitational/teleport/pull/23576
    elif [[ -n "${PROXY_MULTIPLEXING}" ]]; then
        echo "  proxy_listener_mode: ${PROXY_MULTIPLEXING}" >> ${USE_CONFIG_PATH}
    fi

    # enable/start token services and timers
    systemctl_wrap enable teleport-ssm-publish-tokens.service teleport-ssm-publish-tokens.timer
    systemctl_wrap start teleport-ssm-publish-tokens.timer

    # enable/start cert services and timers
    systemctl_wrap enable teleport-get-cert.service teleport-get-cert.timer
    systemctl_wrap enable teleport-renew-cert.service teleport-renew-cert.timer
    systemctl_wrap start --no-block teleport-get-cert.timer
    systemctl_wrap start --no-block teleport-renew-cert.timer

    # enable auth service and disable all-in-one
    systemctl_wrap disable teleport.service
    systemctl_wrap enable teleport-auth.service
    systemctl_wrap start --no-block teleport-auth.service

elif [[ "${TELEPORT_ROLE}" == "proxy" ]]; then
    TUNNEL_LISTEN_PORT=3024
    if [[ "${USE_ACM}" == "true" ]]; then
        echo "proxy-acm" > ${USE_CONFD_DIR}/role.proxy-acm
    else
        echo "proxy" > ${USE_CONFD_DIR}/role.proxy
    fi
    # Teleport proxy proxies and optionally records
    # SSH sessions
    cat >${USE_CONFIG_PATH} <<EOF
version: v3
teleport:
  auth_token: /var/lib/teleport/token
  ca_pin: CA_PIN_HASH_PLACEHOLDER
  nodename: ${LOCAL_HOSTNAME}
  advertise_ip: ${LOCAL_IP}
  cache:
    type: in-memory
  connection_limits:
    max_connections: 65000
    max_users: 10000
  log:
    output: stderr
    severity: INFO
  data_dir: /var/lib/teleport
  storage:
    type: dir
    path: /var/lib/teleport/backend
  auth_server: ${TELEPORT_AUTH_SERVER_LB}:3025

auth_service:
  enabled: no

ssh_service:
  enabled: no

proxy_service:
  enabled: yes
  web_listen_addr: 0.0.0.0:3080
  public_addr: ${TELEPORT_DOMAIN_NAME}:443
EOF

    # don't configure separate listen addresses when TLS routing is enabled
    if [[ "${USE_TLS_ROUTING}" == "true" ]]; then
        # do nothing here
        true
    else
cat >>${USE_CONFIG_PATH} <<EOF
  listen_addr: 0.0.0.0:3023
  tunnel_listen_addr: 0.0.0.0:${TUNNEL_LISTEN_PORT}
EOF
        # write ssh/tunnel config
        write_ssh_and_tunnel_section 443
    fi

    # if we are using Let's Encrypt (i.e. not ACM) then append config lines
    if [[ "${USE_ACM}" != "true" ]]; then
        # enable proxy protocol whenever ACM is not being used
        # it cannot be enabled when there is a layer 7 LB in front of the teleport proxy, as with ACM
        cat >>${USE_CONFIG_PATH} <<EOF
  proxy_protocol: on
EOF
        write_https_keypairs_section
    else
        # trust X-Forwarded-For headers sent by ALB
        cat >>${USE_CONFIG_PATH} <<EOF
  trust_x_forwarded_for: true
EOF
    fi

    # if we are not using TLS routing, then append database/kubernetes sections
    if [[ "${USE_TLS_ROUTING}" != "true" ]]; then
        # set up the database listeners
        write_database_section TELEPORT_DOMAIN_NAME

        # set up the kubernetes listener
        write_kubernetes_section TELEPORT_DOMAIN_NAME
    fi

    # enable/start cert services and timers
    systemctl_wrap enable teleport-check-cert.service teleport-check-cert.timer
    systemctl_wrap start --no-block teleport-check-cert.timer

    # enable proxy service and disable all-in-one
    # skip TLS verification if we are using ACM (as we can't get the cert for use locally, it's on the load balancer)
    systemctl_wrap disable teleport.service
    if [[ "${USE_ACM}" == "true" ]]; then
        systemctl_wrap enable teleport-proxy-acm.service
        systemctl_wrap start --no-block teleport-proxy-acm.service
    else
        systemctl_wrap enable teleport-proxy.service
        systemctl_wrap start --no-block teleport-proxy.service
    fi

elif [[ "${TELEPORT_ROLE}" == "node" ]]; then
    echo "node" > ${USE_CONFD_DIR}/role.node
    # Teleport node handles incoming connections
    cat >${USE_CONFIG_PATH} <<EOF
version: v3
teleport:
  auth_token: /var/lib/teleport/token
  ca_pin: CA_PIN_HASH_PLACEHOLDER
  nodename: ${LOCAL_HOSTNAME}
  advertise_ip: ${LOCAL_IP}
  log:
    output: stderr
    severity: INFO
  data_dir: /var/lib/teleport
  storage:
    type: dir
    path: /var/lib/teleport/backend
  auth_server: ${TELEPORT_AUTH_SERVER_LB}:3025

auth_service:
  enabled: no

ssh_service:
  enabled: yes
  listen_addr: 0.0.0.0:3022

proxy_service:
  enabled: no
EOF

    # enable node service and disable all-in-one
    systemctl_wrap disable teleport.service
    systemctl_wrap enable teleport-node.service
    systemctl_wrap start --no-block teleport-node.service

# starter cluster configuration
elif [[ "${TELEPORT_ROLE}" == "auth,node,proxy" ]]; then
    echo "Teleport all-in-one configuration selected."

    TUNNEL_LISTEN_PORT=3024
    if [[ "${USE_ACM}" == "true" ]]; then
        # always enable TLS routing when ACM is enabled
        USE_TLS_ROUTING=true
        sed -i 's/USE_TLS_ROUTING=false/USE_TLS_ROUTING=true/g' ${USE_CONFD_DIR}/conf
        # remove Let's Encrypt config
        rm -f ${USE_CONFD_DIR}/use-letsencrypt
        sed -i 's/USE_LETSENCRYPT=true/USE_LETSENCRYPT=false/g' ${USE_CONFD_DIR}/conf
        # enable ACM role to use alternative unit file
        rm -f ${USE_CONFD_DIR}/role.all
        echo "all-acm" > ${USE_CONFD_DIR}/role.all-acm
    else
        rm -f ${USE_CONFD_DIR}/role.all-acm
        echo "all" > ${USE_CONFD_DIR}/role.all
    fi

    cat >${USE_CONFIG_PATH} <<EOF
# Auto-generated by /usr/local/bin/teleport-generate-config from values in ${USE_CONFD_DIR}/conf
version: v3
teleport:
  nodename: ${LOCAL_HOSTNAME}
  advertise_ip: ${LOCAL_IP}
  log:
    output: stderr
    severity: INFO
  data_dir: /var/lib/teleport
EOF

    # determine if dynamodb and s3 should be configured, if not, default to dir storage
    if [[ "${TELEPORT_DYNAMO_TABLE_NAME}" != "" && "${TELEPORT_DYNAMO_EVENTS_TABLE_NAME}" != "" && "${TELEPORT_S3_BUCKET}" != "" ]]; then
        echo "Found DynamoDB settings, using DynamoDB and S3 for storage."
        cat >>${USE_CONFIG_PATH} <<EOF
  storage:
    type: dynamodb
    region: ${EC2_REGION}
    table_name: ${TELEPORT_DYNAMO_TABLE_NAME}
    audit_events_uri: dynamodb://${TELEPORT_DYNAMO_EVENTS_TABLE_NAME}
    audit_sessions_uri: s3://${TELEPORT_S3_BUCKET}/records
EOF
    else
        echo "Missing DynamoDB settings, using local dir storage."
        cat >>${USE_CONFIG_PATH} <<EOF
  storage:
    type: dir
    path: /var/lib/teleport/backend
EOF
    fi

    cat >>${USE_CONFIG_PATH} <<EOF

auth_service:
  enabled: yes
  keep_alive_interval: 1m
  keep_alive_count_max: 3
  listen_addr: 0.0.0.0:3025
  authentication:
    ${AUTHENTICATION_STANZA}
  cluster_name: ${TELEPORT_CLUSTER_NAME}
EOF

    # copy and set up license if provided
    if [[ "${TELEPORT_LICENSE_PATH}" != "" ]]; then
        aws ssm get-parameter --with-decryption --name /teleport/${TELEPORT_CLUSTER_NAME}/license --region ${EC2_REGION} --query 'Parameter.Value' --output text > /var/lib/teleport/license.pem
        chown teleport:adm /var/lib/teleport/license.pem
        echo "  license_file: /var/lib/teleport/license.pem" >> ${USE_CONFIG_PATH}
    fi

    # configure proxy_listener_mode
    if [[ "${USE_TLS_ROUTING}" == "true" ]]; then
        echo "  proxy_listener_mode: multiplex" >> ${USE_CONFIG_PATH}
    # support legacy untested method introduced in https://github.com/gravitational/teleport/pull/23576
    elif [[ -n "${PROXY_MULTIPLEXING}" ]]; then
        echo "  proxy_listener_mode: ${PROXY_MULTIPLEXING}" >> ${USE_CONFIG_PATH}
    fi

cat >>${USE_CONFIG_PATH} <<EOF

ssh_service:
  enabled: yes
  listen_addr: 0.0.0.0:3022

proxy_service:
  enabled: yes
  web_listen_addr: 0.0.0.0:443
  public_addr: ${TELEPORT_EXTERNAL_HOSTNAME:-$PUBLIC_IP}:${TELEPORT_EXTERNAL_PORT:-443}
EOF

    # don't configure separate listen addresses when TLS routing is enabled
    if [[ "${USE_TLS_ROUTING}" == "true" ]]; then
        # do nothing here
        true
    else
cat >>${USE_CONFIG_PATH} <<EOF
  listen_addr: 0.0.0.0:3023
  tunnel_listen_addr: 0.0.0.0:${TUNNEL_LISTEN_PORT}
EOF
        # write ssh/tunnel config
        write_ssh_and_tunnel_section 3024
    fi

    # if we are using ACM, enable ACM unit
    if [[ "${USE_ACM}" == "true" ]]; then
        echo "ACM support enabled"

        # don't write database/kubernetes sections if TLS routing is enabled
        if [[ "${USE_TLS_ROUTING}" == "true" ]]; then
            # trust X-Forwarded-For headers sent by ALB
            cat >>${USE_CONFIG_PATH} <<EOF
  trust_x_forwarded_for: true
EOF
        else
            # set up the database listeners
            write_database_section TELEPORT_EXTERNAL_HOSTNAME

            # set up the kubernetes listener
            write_kubernetes_section TELEPORT_EXTERNAL_HOSTNAME
        fi

        systemctl_wrap disable teleport.service
        systemctl_wrap stop --no-block teleport.service

        systemctl_wrap enable teleport-acm.service
        systemctl_wrap start --no-block teleport-acm.service
    # alternatively, if we are using Let's Encrypt, append config lines
    elif [[ "${USE_LETSENCRYPT}" == "true" ]] && [[ "${USE_ACM}" == "false" ]]; then
        echo "Let's Encrypt support enabled"
        # these variables must all be set for Let's Encrypt to work
        # it also needs the running instance to have permissions to read from/write to the S3 bucket
        if [[ "${TELEPORT_DOMAIN_ADMIN_EMAIL}" != "" && "${TELEPORT_DOMAIN_NAME}" != "" && "${TELEPORT_S3_BUCKET}" != "" ]]; then
            write_https_keypairs_section

            # don't write database/kubernetes sections if TLS routing is enabled
            if [[ "${USE_TLS_ROUTING}" == "true" ]]; then
                # do nothing here
                true
            else
                # set up the database listeners
                write_database_section TELEPORT_EXTERNAL_HOSTNAME

                # set up the kubernetes listener
                write_kubernetes_section TELEPORT_EXTERNAL_HOSTNAME
            fi

            # enable/start cert services and timers
            systemctl_wrap enable teleport-get-cert.service teleport-get-cert.timer
            systemctl_wrap start teleport-get-cert.timer

            systemctl_wrap enable teleport-renew-cert.service teleport-renew-cert.timer
            systemctl_wrap start --no-block teleport-renew-cert.timer

            systemctl_wrap enable teleport-check-cert.service teleport-check-cert.timer
            systemctl_wrap start --no-block teleport-check-cert.timer

            systemctl_wrap start --no-block teleport-get-cert.service
        fi
    fi

else
    echo "No Teleport role provided via TELEPORT_ROLE; using generic all-in-one config"
    TUNNEL_LISTEN_PORT=3024

    if [[ "${USE_ACM}" == "true" ]]; then
        rm -f ${USE_CONFD_DIR}/use-letsencrypt
        rm -f ${USE_CONFD_DIR}/role.all
        echo "all-acm" > ${USE_CONFD_DIR}/role.all-acm
    else
        rm -f ${USE_CONFD_DIR}/role.all-acm
        echo "all" > ${USE_CONFD_DIR}/role.all
    fi

    cat >${USE_CONFIG_PATH} <<EOF
version: v3
teleport:
  nodename: ${LOCAL_HOSTNAME}
  advertise_ip: ${LOCAL_IP}
  log:
    output: stderr
    severity: INFO
  data_dir: /var/lib/teleport
  storage:
    type: dir
    path: /var/lib/teleport/backend

auth_service:
  enabled: yes
  keep_alive_interval: 1m
  keep_alive_count_max: 3
  listen_addr: 0.0.0.0:3025
  authentication:
    ${AUTHENTICATION_STANZA}
  cluster_name: ${TELEPORT_CLUSTER_NAME}

ssh_service:
  enabled: yes
  listen_addr: 0.0.0.0:3022

proxy_service:
  enabled: yes
  web_listen_addr: 0.0.0.0:443
  public_addr: ${TELEPORT_EXTERNAL_HOSTNAME:-$PUBLIC_IP}:${TELEPORT_EXTERNAL_PORT:-443}
EOF

    # don't configure separate listen addresses when TLS routing is enabled
    if [[ "${USE_TLS_ROUTING}" == "true" ]]; then
        # do nothing here
        true
    else
        cat >${USE_CONFIG_PATH} <<EOF
  listen_addr: 0.0.0.0:3023
  tunnel_listen_addr: 0.0.0.0:${TUNNEL_LISTEN_PORT}
EOF

        # write ssh/tunnel config
        write_ssh_and_tunnel_section 3024

        # set up the database listeners
        write_database_section TELEPORT_EXTERNAL_HOSTNAME

        # set up the kubernetes listener
        write_kubernetes_section TELEPORT_EXTERNAL_HOSTNAME
    fi

    # if we are using Let's Encrypt, append config lines
    if [[ "${USE_LETSENCRYPT}" == "true" ]]; then
        echo "Let's Encrypt support enabled"
        # these variables must all be set for Let's Encrypt to work
        # it also needs the running instance to have permissions to read from/write to the S3 bucket
        if [[ "${TELEPORT_DOMAIN_ADMIN_EMAIL}" != "" && "${TELEPORT_DOMAIN_NAME}" != "" && "${TELEPORT_S3_BUCKET}" != "" ]]; then
            write_https_keypairs_section

            # enable/start cert services and timers
            systemctl_wrap enable teleport-get-cert.service teleport-get-cert.timer
            systemctl_wrap start teleport-get-cert.timer

            systemctl_wrap enable teleport-renew-cert.service teleport-renew-cert.timer
            systemctl_wrap start --no-block teleport-renew-cert.timer

            systemctl_wrap enable teleport-check-cert.service teleport-check-cert.timer
            systemctl_wrap start --no-block teleport-check-cert.timer

            systemctl_wrap start --no-block teleport-get-cert.service
        fi
    elif [[ "${USE_ACM}" == "true" ]]; then
        echo "ACM support enabled"

        systemctl_wrap disable teleport.service
        systemctl_wrap stop --no-block teleport.service

        systemctl_wrap enable teleport-acm.service
        systemctl_wrap start --no-block teleport-acm.service
    fi
fi

# handle configuration for agent mode
if [[ "${TELEPORT_ROLE}" == "agent" ]]; then
    echo "agent" > ${USE_CONFD_DIR}/role.agent
    cat >${USE_CONFIG_PATH} <<EOF
version: v3
teleport:
  log:
    output: stderr
    severity: INFO
  data_dir: /var/lib/teleport
  auth_token: ${TELEPORT_JOIN_TOKEN}
  proxy_server: ${TELEPORT_PROXY_SERVER_LB}

auth_service:
  enabled: no
proxy_service:
  enabled: no
EOF

  # enable SSH service
  if [[ "${TELEPORT_AGENT_SSH_ENABLED}" == "true" ]]; then
    cat >>${USE_CONFIG_PATH} <<EOF
ssh_service:
  enabled: yes
EOF
    # add SSH labels
    if [[ "${TELEPORT_AGENT_SSH_LABELS}" != "" ]]; then
      # replace | with a literal newline and space
      TELEPORT_AGENT_SSH_LABELS_EXPANDED="${TELEPORT_AGENT_SSH_LABELS//|/
    }"
      cat >>${USE_CONFIG_PATH} <<EOF
  labels:
    ${TELEPORT_AGENT_SSH_LABELS_EXPANDED}
EOF
    fi
  else
    # we have to explicitly disable the SSH service if it's not being used, as it historically defaults to enabled
    cat >>${USE_CONFIG_PATH} <<EOF
  ssh_service:
    enabled: no
EOF
  fi

  # enable database service
  if [[ "${TELEPORT_AGENT_DB_ENABLED}" == "true" ]]; then
    cat >>${USE_CONFIG_PATH} <<EOF
db_service:
  enabled: yes
  databases:
  - name: ${TELEPORT_AGENT_DB_NAME}
    description: "${TELEPORT_AGENT_DB_DESCRIPTION:-""}"
    protocol: ${TELEPORT_AGENT_DB_PROTOCOL}
    uri: "${TELEPORT_AGENT_DB_URI}"
    aws:
      region: ${TELEPORT_AGENT_DB_REGION:-$EC2_REGION}
EOF
    # add Redshift-specific config
    if [[ "${TELEPORT_AGENT_DB_REDSHIFT_CLUSTER_ID}" != "" ]]; then
          cat >>${USE_CONFIG_PATH} <<EOF
      redshift:
        cluster_id: "${TELEPORT_AGENT_DB_REDSHIFT_CLUSTER_ID}"
EOF
    fi
    # add Database labels
    if [[ "${TELEPORT_AGENT_DB_LABELS}" != "" ]]; then
      # replace | with a literal newline and space
      TELEPORT_AGENT_DB_LABELS_EXPANDED="${TELEPORT_AGENT_DB_LABELS//|/
      }"
      cat >>${USE_CONFIG_PATH} <<EOF
    static_labels:
      ${TELEPORT_AGENT_DB_LABELS_EXPANDED}
EOF
    fi
  fi

# enable app service
  if [[ "${TELEPORT_AGENT_APP_ENABLED}" == "true" ]]; then
    cat >>${USE_CONFIG_PATH} <<EOF
app_service:
  enabled: yes
  apps:
  - name: ${TELEPORT_AGENT_APP_NAME}
    description: "${TELEPORT_AGENT_APP_DESCRIPTION:-""}"
    uri: "${TELEPORT_AGENT_APP_URI}"
EOF
    # add public_addr if set
    if [[ "${TELEPORT_AGENT_APP_PUBLIC_ADDR}" != "" ]]; then
      cat >>${USE_CONFIG_PATH} <<EOF
    public_addr: "${TELEPORT_AGENT_APP_PUBLIC_ADDR}"
EOF
    fi
    # add insecure_skip_verify if set
    if [[ "${TELEPORT_AGENT_APP_INSECURE_SKIP_VERIFY}" == "true" ]]; then
      cat >>${USE_CONFIG_PATH} <<EOF
    insecure_skip_verify: true
EOF
    fi
    # add app labels
    if [[ "${TELEPORT_AGENT_APP_LABELS}" != "" ]]; then
      # replace | with a literal newline and space
      TELEPORT_AGENT_APP_LABELS_EXPANDED="${TELEPORT_AGENT_APP_LABELS//|/
      }"
      cat >>${USE_CONFIG_PATH} <<EOF
    labels:
      ${TELEPORT_AGENT_APP_LABELS_EXPANDED}
EOF
    fi
  fi

  # enable service
  systemctl_wrap start --no-block teleport.service
fi

# make sure config file can be edited by pre-start commands running later (assuming it exists)
if [ -f ${USE_CONFIG_PATH} ]; then
    if getent passwd teleport >/dev/null 2>&1 && getent group adm >/dev/null 2>&1; then
        chown teleport:adm ${USE_CONFIG_PATH}
    fi
fi
