mirror of
https://gitlab.alpinelinux.org/alpine/cloud/tiny-cloud.git
synced 2025-12-16 11:52:43 +03:00
Introducing Tiny Cloud
This commit is contained in:
parent
0bfdd16977
commit
07f0e646e8
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*~
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
.vscode/
|
||||||
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2017-2020 Michael Crute
|
Copyright (c) 2017-2021 Jake Buchholz Göktürk, Michael Crute
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
|||||||
3
Makefile
3
Makefile
@ -2,4 +2,5 @@ PREFIX?=/
|
|||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install:
|
install:
|
||||||
install -Dm 755 tiny-ec2-bootstrap $(PREFIX)/etc/init.d/tiny-ec2-bootstrap
|
cp -r etc $(PREFIX)
|
||||||
|
cp -r lib $(PREFIX)
|
||||||
|
|||||||
165
README.md
165
README.md
@ -1,70 +1,125 @@
|
|||||||
# Tiny EC2 Bootstrapper
|
# Tiny Cloud
|
||||||
|
|
||||||
This is designed to do the minimal amount of work required to bootstrap an EC2
|
The Tiny Cloud bootstrapper performs critical initialization tasks for cloud
|
||||||
instance based on the local settings assigned at boot time as well as the
|
instances during their first boot. Unlike the more popular and feature-rich
|
||||||
user's configured settings. This is, in-concept, similar to
|
[cloud-init](https://cloudinit.readthedocs.io/en/latest), Tiny Cloud seeks to
|
||||||
[cloud-init](https://cloudinit.readthedocs.io/en/latest/) but trades features
|
do just what is necessary with a small footprint and minimal dependencies.
|
||||||
and cloud platform support for small size and limited external dependencies.
|
|
||||||
|
A direct descendant of [tiny-ec2-bootstrap](
|
||||||
|
https://gitlab.alpinelinux.org/alpine/cloud/tiny-ec2-bootstrap), Tiny Cloud
|
||||||
|
works with multiple cloud providers. Currently, the following are supported:
|
||||||
|
* AWS (Amazon Web Services)
|
||||||
|
* Azure (Microsoft Azure)
|
||||||
|
* GCP (Google Cloud Platform)
|
||||||
|
* OCI (Oracle Cloud Infrastructure)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
The following actions will occur ***only once***, during the initial boot of an
|
||||||
|
instance:
|
||||||
|
* expand the root filesystem to use all available root device space, during the
|
||||||
|
**sysinit** runlevel
|
||||||
|
* set the instance's hostname from instance metadata
|
||||||
|
* install SSH keys from instance metadata to the cloud user account's
|
||||||
|
`authorized_keys` file (the user must already exist)
|
||||||
|
* save the instance user-data to a file and if it's a script, execute it at the
|
||||||
|
end of the **default** runlevel
|
||||||
|
|
||||||
|
Optional features, which may not be universally necessary:
|
||||||
|
* manage symlinks from NVMe block devices to `/dev/xvd` and `/dev/sd` devices
|
||||||
|
(i.e. AWS Nitro instances)
|
||||||
|
* manage secondary IPv4 and IPv6 addresses on network interfaces
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
The most important feature of this bootstrapper is the very limited set of
|
As Tiny Cloud is meant to be tiny, it has very few dependencies:
|
||||||
dependencies. In-fact, this works with just BusyBox (provided ash and wget
|
* Busybox (`ash`, `wget`, etc.)
|
||||||
are built in) and a couple utilities for expanding the root filesystem.
|
* `partx`
|
||||||
The full list of required dependencies are:
|
* `resize2fs`
|
||||||
|
* `sfdisk`
|
||||||
|
|
||||||
- bash-like shell (e.g. bash, dash, ash)
|
Tiny Cloud has been developed specifically for use with the
|
||||||
- wget
|
[Alpine Cloud Images](https://gitlab.alpinelinux.org/alpine/cloud/alpine-cloud-images)
|
||||||
- sfdisk
|
project, and as such, it is currently tailored for use with [Alpine Linux](
|
||||||
- partx
|
https://alpinelinux.org), the [OpenRC](https://github.com/OpenRC/openrc) init
|
||||||
- resize2fs
|
system, and the `ext4` root filesystem. If you would like to see Tiny Cloud
|
||||||
|
supported on additional distributions, init systems, and/or filesystems, please
|
||||||
|
open an issue with your request -- or better yet, submit a merge request!
|
||||||
|
|
||||||
## Supported Features and Environments
|
## Installation
|
||||||
|
|
||||||
cloud-init has support for many different cloud providers. This project only
|
Typically, Tiny Cloud is installed and configured when building a cloud image,
|
||||||
supports EC2; [EC2 metadata
|
and is available on Alpine Linux as the [`tiny-cloud`](
|
||||||
service](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)
|
https://pkgs.alpinelinux.org/packages?name=tiny-cloud) APK...
|
||||||
is a hard requirement of using this bootstrapper. All of the data for the
|
|
||||||
supported features below is sourced from the EC2 instance metadata service
|
|
||||||
which runs on every EC2 instance at IP 169.254.169.254.
|
|
||||||
|
|
||||||
cloud-init also has a very rich feature set with support for adding users,
|
|
||||||
installing packages, and many other things. This bootstrap does not support
|
|
||||||
those things. Instead it supports:
|
|
||||||
|
|
||||||
- setting system hostname
|
|
||||||
- installing the instance's SSH keys in the EC2 user's authorized_keys file
|
|
||||||
- running any script-like user data (must start with #!)
|
|
||||||
- disabling root and the EC2 user's password
|
|
||||||
- expanding root partition to available disk space
|
|
||||||
|
|
||||||
These steps only run once. After the initial bootstrap the bootstrapper script
|
|
||||||
is a no-op. To force the script to run again at boot time remove the file
|
|
||||||
`/var/lib/cloud/.bootstrap-complete` and reboot the instance.
|
|
||||||
|
|
||||||
The default EC2 user is `alpine`; this can be overriden with a
|
|
||||||
`/etc/conf.d/tiny-ec2-bootstrap` containing...
|
|
||||||
```
|
```
|
||||||
EC2_USER="otheruser"
|
apk install tiny-cloud
|
||||||
```
|
```
|
||||||
The EC2 user *must* already exist in the AMI -- `tiny-ec2-bootstrap` will
|
This will install the necessary init scripts, libraries, etc. plus any missing
|
||||||
**NOT** add the user automatically.
|
dependencies.
|
||||||
|
|
||||||
## User Data
|
Alternately, you can download a release tarball, and use `make` to install it.
|
||||||
|
|
||||||
User data is provided at instance boot time and can be any arbitrary string of
|
Next, enable the three primary init scripts...
|
||||||
data. The bootstrapper will consider any user data that begins with the ASCII
|
```
|
||||||
characters `#!` to be a script. It will write the entire contents of the user
|
rc-update add tiny-cloud-early sysinit
|
||||||
data to `/var/lib/cloud/user-data.sh`, make the file executable, and execute
|
rc-update add tiny-cloud default
|
||||||
the file piping any output to `/var/log/cloud-bootstrap.log`.
|
rc-update add tiny-cloud-final default
|
||||||
|
```
|
||||||
|
|
||||||
The user data script can do anything it pleases with the instance. It will be
|
## Configuration
|
||||||
run as root and networking will be up. No other guarantees about system state
|
|
||||||
are made at the point the script runs.
|
|
||||||
|
|
||||||
## Assumptions
|
Tiny Cloud looks expects configuration to be found at
|
||||||
|
[`/etc/conf.d/tiny-cloud`](etc/conf.d/tiny-cloud), which documents all
|
||||||
|
tuneable parameters (and their defaults).
|
||||||
|
|
||||||
- This was written for Alpine Linux; use on other distributions has not been
|
However, because Tiny Cloud does not do auto-detection, you ***must*** set a
|
||||||
tested.
|
value for `CLOUD` indicating which cloud provider which will be used when
|
||||||
|
instantiating the image. Current valid values are `aws`, `azure`, `gcp`, and
|
||||||
|
`oci`.
|
||||||
|
|
||||||
- The script is run by OpenRC.
|
## Operation
|
||||||
|
|
||||||
|
The first time an instance boots -- either freshly instantiated from an image,
|
||||||
|
or after installation on an existing instance -- Tiny Cloud sets up the
|
||||||
|
instance in three phases...
|
||||||
|
|
||||||
|
### Early Phase
|
||||||
|
|
||||||
|
The `tiny-cloud-early` init script does not depend on the cloud provider's
|
||||||
|
Instance MetaData Service (IMDS), and does therefore does not have a dependency
|
||||||
|
on networking. During this "early" phase, the root filesystem is expanded, and
|
||||||
|
any necessary `mdev` rules for device hotplug are set up.
|
||||||
|
|
||||||
|
### Main Phase
|
||||||
|
|
||||||
|
The main `tiny-cloud` init script *does* depend on the cloud provider's IMDS
|
||||||
|
data, and sets up instance's hostname and the cloud user's SSH keys before
|
||||||
|
`sshd` starts.
|
||||||
|
|
||||||
|
### Final Phase
|
||||||
|
|
||||||
|
`tiny-cloud-final` should be the very last init script to run in the
|
||||||
|
**default** runlevel, and saves the instance's user data to
|
||||||
|
`/var/lib/cloud/user-data`.
|
||||||
|
|
||||||
|
If the user data is a script that starts with `#!` (aka "[shebang](
|
||||||
|
https://en.wikipedia.org/wiki/Shebang_(Unix))"), it will be executed; its
|
||||||
|
output (combined STDOUT and STDERR) is saved to `/var/log/cloud/user-data.log`
|
||||||
|
and the exit code can be found in `/var/log/cloud/user-data.exit`.
|
||||||
|
|
||||||
|
If all went well, the very last thing `tiny-cloud-final` does is touch
|
||||||
|
`/var/lib/cloud/.bootstrap-complete` into existence.
|
||||||
|
|
||||||
|
### Further Reboots
|
||||||
|
|
||||||
|
After the initial bootstrap of an instance, the init scripts are largely a
|
||||||
|
no-op.
|
||||||
|
|
||||||
|
To force the init scripts to re-run on the next boot...
|
||||||
|
```
|
||||||
|
rm -f /var/lib/cloud/.bootstrap-complete
|
||||||
|
```
|
||||||
|
If you're instantiating an instance in order to create a new cloud image
|
||||||
|
(using [Packer](https://packer.io), or some other means), you will need to
|
||||||
|
remove this file before creating the image to ensure that instances using the
|
||||||
|
new image will also run Tiny Cloud init scripts during their first boot.
|
||||||
|
|||||||
10
etc/conf.d/tiny-cloud
Normal file
10
etc/conf.d/tiny-cloud
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# tiny-cloud configuration
|
||||||
|
|
||||||
|
# REQUIRED: The instance's cloud provider (valid: aws, azure, gcp, oci)
|
||||||
|
CLOUD=
|
||||||
|
|
||||||
|
# User account where instance SSH keys are installed
|
||||||
|
#CLOUD_USER=alpine
|
||||||
|
|
||||||
|
# Number of seconds an AWS IMDS token is valid
|
||||||
|
#IMDS_TOKEN_TTL=5
|
||||||
51
etc/init.d/tiny-cloud
Normal file
51
etc/init.d/tiny-cloud
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/sbin/openrc-run
|
||||||
|
# vim:set ft=sh noet ts=4:
|
||||||
|
|
||||||
|
description="Tiny Cloud Bootstrap - main phase"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need net
|
||||||
|
before sshd
|
||||||
|
}
|
||||||
|
|
||||||
|
_update_hostname() {
|
||||||
|
local fqdn=$(imds_hostname)
|
||||||
|
local host="${fqdn%%\.*}"
|
||||||
|
echo "$host" > /etc/hostname
|
||||||
|
hostname -F /etc/hostname
|
||||||
|
echo -e "127.0.1.1\t$fqdn $host" >> /etc/hosts
|
||||||
|
}
|
||||||
|
|
||||||
|
_set_ssh_keys() {
|
||||||
|
local user="$1"
|
||||||
|
local pwent=$(getent passwd "$user")
|
||||||
|
local group=$(echo "$pwent" | cut -d: -f4)
|
||||||
|
local ssh_dir="$(echo "$pwent" | cut -d: -f6)/.ssh"
|
||||||
|
local keys_file="$ssh_dir/authorized_keys"
|
||||||
|
|
||||||
|
if [ ! -d "$ssh_dir" ]; then
|
||||||
|
mkdir -p "$ssh_dir"
|
||||||
|
chmod 700 "$ssh_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch "$keys_file"
|
||||||
|
chmod 600 "$keys_file"
|
||||||
|
chown -R "$user:$group" "$ssh_dir"
|
||||||
|
imds_ssh_keys > "$keys_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
[ -f "/var/lib/cloud/.bootstrap-complete" ] && return 0
|
||||||
|
[ -d "/var/lib/cloud" ] || mkdir -p /var/lib/cloud
|
||||||
|
|
||||||
|
source /var/lib/tiny-cloud/imds
|
||||||
|
CLOUD_USER="${CLOUD_USER:-alpine}"
|
||||||
|
|
||||||
|
ebegin "Setting Instance Hostname"; _update_hostname; eend $?
|
||||||
|
|
||||||
|
ebegin "Installing SSH Keys for $CLOUD_USER User"
|
||||||
|
_set_ssh_keys "$CLOUD_USER"
|
||||||
|
eend $?
|
||||||
|
|
||||||
|
# TODO: background tiny-cloud-network watcher?
|
||||||
|
}
|
||||||
36
etc/init.d/tiny-cloud-early
Normal file
36
etc/init.d/tiny-cloud-early
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#!/sbin/openrc-run
|
||||||
|
# vim:set ft=sh noet ts=4:
|
||||||
|
|
||||||
|
description="Tiny Cloud Bootstrap - early phase"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
before mdev
|
||||||
|
}
|
||||||
|
|
||||||
|
_expand_root_partition() {
|
||||||
|
local mountpoint=$(busybox mountpoint -n / | cut -d' ' -f1)
|
||||||
|
local volume=$(echo "$mountpoint" |
|
||||||
|
sed -Ee "s/(nvme\d+n\d|(xv|s)d[a-z])p?\d?$/\1/"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "$mountpoint" != "$volume" ]; then
|
||||||
|
partition=$(echo "$volume" | sed -Ee "s/.*(\d+)$/\2/")
|
||||||
|
einfo "Expanding root partition to volume size..."
|
||||||
|
echo ", +" | sfdisk -q --no-reread -N "$partition" "$volume"
|
||||||
|
einfo "Updating kernel with new partition table..."
|
||||||
|
partx -u "$volume"
|
||||||
|
fi
|
||||||
|
einfo "Resizing root filesystem..."
|
||||||
|
resize2fs "$mountpoint"
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
[ -f "/var/lib/cloud/.bootstrap-complete" ] && return 0
|
||||||
|
[ -d "/var/lib/cloud" ] || mkdir -p /var/lib/cloud
|
||||||
|
|
||||||
|
source /etc/conf.d/tiny-cloud # or use auto-sourced tiny-cloud-early?
|
||||||
|
|
||||||
|
ebegin "Expanding Root Partition"; _expand_root_partition; eend $?
|
||||||
|
|
||||||
|
# TODO: _setup mdev things, if applicable
|
||||||
|
}
|
||||||
37
etc/init.d/tiny-cloud-final
Normal file
37
etc/init.d/tiny-cloud-final
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#!/sbin/openrc-run
|
||||||
|
# vim:set ft=sh noet ts=4:
|
||||||
|
|
||||||
|
description="Tiny Cloud Bootstrap - final phase"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
after *
|
||||||
|
provide cloud-final
|
||||||
|
}
|
||||||
|
|
||||||
|
_save_userdata() {
|
||||||
|
imds_userdata > "$USERDATA"
|
||||||
|
}
|
||||||
|
|
||||||
|
_run_userdata() {
|
||||||
|
local log="/var/log/user-data.log"
|
||||||
|
local exit="/var/log/user-data.exit"
|
||||||
|
|
||||||
|
chmod +x "$USERDATA"
|
||||||
|
{ "$USERDATA" 2>& 1; echo $? > "$exit"; } | tee "$log"
|
||||||
|
|
||||||
|
return $(cat "$exit")
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
[ -f "/var/lib/cloud/.bootstrap-complete" ] && return 0
|
||||||
|
[ -d "/var/lib/cloud" ] || mkdir -p /var/lib/cloud
|
||||||
|
|
||||||
|
source /lib/tiny-cloud/imds
|
||||||
|
|
||||||
|
USERDATA="/var/lib/cloud/user-data"
|
||||||
|
|
||||||
|
ebegin "Saving Instance UserData"; _save_userdata; eend $?
|
||||||
|
if head -n1 "$USERDATA" | grep -q '^#!/'; then
|
||||||
|
ebegin "Executing UserData Script"; _run_userdata; eend $?
|
||||||
|
fi
|
||||||
|
}
|
||||||
28
lib/tiny-cloud/aws/imds
Normal file
28
lib/tiny-cloud/aws/imds
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# AWS Instance MetaData Service related variables and functions
|
||||||
|
# vim:set ft=sh noet ts=4:
|
||||||
|
|
||||||
|
IMDS_HEADER="X-aws-ec2-metadata-token"
|
||||||
|
IMDS_TOKEN_TTL_HEADER="X-aws-ec2-metadata-token-ttl-seconds"
|
||||||
|
IMDS_TOKEN_TTL=${IMDS_TOKEN_TTL:-5}
|
||||||
|
IMDS_URL="http://169.254.169.254/latest"
|
||||||
|
|
||||||
|
IMDS_HOSTNAME="meta-data/hostname"
|
||||||
|
IMDS_SSH_KEYS="meta-data/public-keys"
|
||||||
|
IMDS_USERDATA="user-data"
|
||||||
|
|
||||||
|
_imds_token() {
|
||||||
|
echo -ne "PUT /latest/api/token HTTP/1.0\r\n"\
|
||||||
|
"$IMDS_TOKEN_TTL_HEADER: $IMDS_TOKEN_TTL\r\n\r\n" |
|
||||||
|
nc 169.254.169.254 80 | tail -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_imds_header() {
|
||||||
|
echo "$IMDS_HEADER: $(_imds_token)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# digs deeper than the default
|
||||||
|
imds_ssh_keys() {
|
||||||
|
for key in $(imds "$IMDS_SSH_KEYS"); do
|
||||||
|
imds "$IMDS_SSH_KEYS/${key%=*}/openssh-key"
|
||||||
|
done | sort -u
|
||||||
|
}
|
||||||
26
lib/tiny-cloud/azure/imds
Normal file
26
lib/tiny-cloud/azure/imds
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Azure metadata-related variables and functions
|
||||||
|
# vim:set ft=sh noet ts=4:
|
||||||
|
|
||||||
|
IMDS_HEADER="Metadata"
|
||||||
|
IMDS_QUERY="?format=text&api-version=2021-05-01"
|
||||||
|
IMDS_URL="http://169.254.169.254/metadata/instance"
|
||||||
|
|
||||||
|
IMDS_HOSTNAME="compute/name"
|
||||||
|
IMDS_SSH_KEYS="compute/publicKeys"
|
||||||
|
IMDS_USERDATA="compute/userData"
|
||||||
|
|
||||||
|
_imds_header() {
|
||||||
|
echo "$IMDS_HEADER: true"
|
||||||
|
}
|
||||||
|
|
||||||
|
# dig deeper than default
|
||||||
|
imds_ssh_keys() {
|
||||||
|
for key in $(imds "$IMDS_SSH_KEYS"); do
|
||||||
|
imds "$IMDS_SSH_KEYS/${key}/keyData"
|
||||||
|
done | sort -u
|
||||||
|
}
|
||||||
|
|
||||||
|
# decode userdata value
|
||||||
|
imds_userdata() {
|
||||||
|
imds "$IMDS_USERDATA" | base64 -d | _enforce_ending_newline
|
||||||
|
}
|
||||||
24
lib/tiny-cloud/gcp/imds
Normal file
24
lib/tiny-cloud/gcp/imds
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Google Cloud metadata-related variables and functions
|
||||||
|
# vim:set ft=sh noet ts=4:
|
||||||
|
|
||||||
|
IMDS_HEADER="Metadata-Flavor"
|
||||||
|
IMDS_URL="http://169.254.169.254/computeMetadata/v1"
|
||||||
|
|
||||||
|
IMDS_HOSTNAME="instance/hostname"
|
||||||
|
IMDS_SSH_KEYS="
|
||||||
|
project/attributes/ssh-keys
|
||||||
|
instance/attributes/ssh-keys
|
||||||
|
"
|
||||||
|
IMDS_USERDATA="instance/attributes/user-data"
|
||||||
|
|
||||||
|
_imds_header() {
|
||||||
|
echo "$IMDS_HEADER: Google"
|
||||||
|
}
|
||||||
|
|
||||||
|
# merge project and instance keys
|
||||||
|
imds_ssh_keys() {
|
||||||
|
for ssh_keys in $IMDS_SSH_KEYS; do
|
||||||
|
# ignore errors and strip leading '<login>:'
|
||||||
|
_imds "$ssh_keys" | cut -d: -f2- | _enforce_ending_newline
|
||||||
|
done | sort -u
|
||||||
|
}
|
||||||
60
lib/tiny-cloud/imds
Normal file
60
lib/tiny-cloud/imds
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# tiny-cloud Instance MetaData Service related functions and variables
|
||||||
|
# vim:set ft=sh ts=4 noet:
|
||||||
|
|
||||||
|
CONF_DIR="/etc/conf.d"
|
||||||
|
LIB_DIR="/lib/tiny-cloud"
|
||||||
|
|
||||||
|
### load configuration
|
||||||
|
|
||||||
|
[ -f "$CONF_DIR"/tiny-cloud ] && source "$CONF_DIR"/tiny-cloud
|
||||||
|
|
||||||
|
### configuration defaults
|
||||||
|
|
||||||
|
CLOUD="${CLOUD:-unknown}"
|
||||||
|
CLOUD_LOGIN="${CLOUD_LOGIN:-alpine}"
|
||||||
|
if [ ! -d "$LIB_DIR/$CLOUD" ]; then
|
||||||
|
echo "ERROR: Unknown Cloud '$CLOUD'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
IMDS_HEADER=
|
||||||
|
IMDS_QUERY=
|
||||||
|
IMDS_URL=
|
||||||
|
|
||||||
|
IMDS_HOSTNAME=
|
||||||
|
IMDS_SSH_KEYS=
|
||||||
|
IMDS_USERDATA=
|
||||||
|
|
||||||
|
### default/common functions
|
||||||
|
|
||||||
|
_imds() {
|
||||||
|
wget --quiet --header "$(_imds_header)" --output-document - \
|
||||||
|
"$IMDS_URL/$1/$IMDS_QUERY"
|
||||||
|
}
|
||||||
|
|
||||||
|
_enforce_ending_newline() { sed '$a\'; }
|
||||||
|
|
||||||
|
imds() {
|
||||||
|
set -o pipefail
|
||||||
|
_imds "$1" | _enforce_ending_newline
|
||||||
|
RV=$?
|
||||||
|
set +o pipefail
|
||||||
|
[ $RV != 0 ] && echo "ERROR: $RV" >&2
|
||||||
|
return $RV
|
||||||
|
}
|
||||||
|
|
||||||
|
imds_hostname() {
|
||||||
|
imds "$IMDS_HOSTNAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
imds_userdata() {
|
||||||
|
imds "$IMDS_USERDATA"
|
||||||
|
}
|
||||||
|
|
||||||
|
imds_ssh_keys() {
|
||||||
|
imds "$IMDS_SSH_KEYS"
|
||||||
|
}
|
||||||
|
|
||||||
|
# cloud-specific things (potentially overriding the above)
|
||||||
|
|
||||||
|
[ -f "$LIB_DIR/$CLOUD/metadata" ] && source "$LIB_DIR/$CLOUD"/imds
|
||||||
13
lib/tiny-cloud/oci/imds
Normal file
13
lib/tiny-cloud/oci/imds
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# OCI metadata-related variables and functions
|
||||||
|
# vim:set ft=sh noet ts=4:
|
||||||
|
|
||||||
|
IMDS_HEADER="Authorization"
|
||||||
|
IMDS_URL="http://169.254.169.254/opc/v2"
|
||||||
|
|
||||||
|
IMDS_HOSTNAME="instance/hostname"
|
||||||
|
IMDS_SSH_KEYS="instance/metadata/ssh_authorized_keys"
|
||||||
|
IMDS_USERDATA="instance/metadata/userdata"
|
||||||
|
|
||||||
|
_imds_header() {
|
||||||
|
echo "$IMDS_HEADER: Bearer Oracle"
|
||||||
|
}
|
||||||
@ -1,110 +0,0 @@
|
|||||||
#!/sbin/openrc-run
|
|
||||||
# vim:set ft=sh noet ts=4:
|
|
||||||
|
|
||||||
description="Provides EC2 cloud bootstrap"
|
|
||||||
|
|
||||||
# override in /etc/conf.d/tiny-ec2-bootstrap
|
|
||||||
EC2_USER=${EC2_USER:-alpine}
|
|
||||||
IMDS2_TOKEN_TTL=${IMDS2_TOKEN_TTL:-5}
|
|
||||||
|
|
||||||
depend() {
|
|
||||||
need net
|
|
||||||
provide cloud-final
|
|
||||||
}
|
|
||||||
|
|
||||||
_get_metadata_token() {
|
|
||||||
echo -ne "PUT /latest/api/token HTTP/1.0\r\nX-aws-ec2-metadata-token-ttl-seconds: $IMDS2_TOKEN_TTL\r\n\r\n" |
|
|
||||||
nc 169.254.169.254 80 | tail -n 1
|
|
||||||
}
|
|
||||||
|
|
||||||
_get_metadata() {
|
|
||||||
local uri="$1"
|
|
||||||
wget -qO - --header "X-aws-ec2-metadata-token: $(_get_metadata_token)" \
|
|
||||||
"http://169.254.169.254/latest/$uri" 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
_update_hostname() {
|
|
||||||
local ec2_fqdn="$(_get_metadata meta-data/hostname)"
|
|
||||||
local short_hostname="${ec2_fqdn%%\.*}"
|
|
||||||
echo "$short_hostname" > /etc/hostname
|
|
||||||
hostname -F /etc/hostname
|
|
||||||
echo -e "127.0.1.1\t$ec2_fqdn $short_hostname" >> /etc/hosts
|
|
||||||
}
|
|
||||||
|
|
||||||
_set_ssh_keys() {
|
|
||||||
local user="$1"
|
|
||||||
local group="$(getent passwd "$user" | cut -d: -f4)"
|
|
||||||
local ssh_dir="$(getent passwd "$user" | cut -d: -f6)/.ssh"
|
|
||||||
local keys_file="$ssh_dir/authorized_keys"
|
|
||||||
|
|
||||||
if [ ! -d "$ssh_dir" ]; then
|
|
||||||
mkdir -p "$ssh_dir"
|
|
||||||
chmod 755 "$ssh_dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -f "$keys_file" ] && rm "$keys_file"
|
|
||||||
|
|
||||||
touch "$keys_file"
|
|
||||||
chmod 600 "$keys_file"
|
|
||||||
chown -R "$user:$group" "$ssh_dir"
|
|
||||||
|
|
||||||
for key in $(_get_metadata meta-data/public-keys/); do
|
|
||||||
_get_metadata "meta-data/public-keys/${key%=*}/openssh-key/" >> "$keys_file"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
_run_userdata() {
|
|
||||||
local user_data="$(_get_metadata user-data)"
|
|
||||||
if printf '%s' "$user_data" | head -n1 | grep -q '^#!/'; then
|
|
||||||
printf '%s' "$user_data" >/var/lib/cloud/user-data.sh
|
|
||||||
chmod +x /var/lib/cloud/user-data.sh
|
|
||||||
|
|
||||||
local log_file=/var/log/cloud-bootstrap.log
|
|
||||||
local ec_file=/var/log/cloud-bootstrap.exit
|
|
||||||
|
|
||||||
{ /var/lib/cloud/user-data.sh 2>&1 ; echo $? >"$ec_file"; } | tee "$log_file"
|
|
||||||
ec=$(cat "$ec_file")
|
|
||||||
|
|
||||||
echo "User Data Script Exit Status: $ec"
|
|
||||||
return "$ec"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_resize_root_partition() {
|
|
||||||
local mountpoint="$(busybox mountpoint -n / | cut -d' ' -f1)"
|
|
||||||
|
|
||||||
# mountpoint is the second partition...
|
|
||||||
if echo "$mountpoint" | cut -d' ' -f1 | grep -qE '/(nvme\d+n\d+p|xvd[a-z]+)2$'; then
|
|
||||||
local volume="$(echo "$mountpoint" | sed -Ee 's/(nvme\d+n\d+|xvd[a-z]+)p?2/\1/')"
|
|
||||||
einfo "Expanding root partition to volume size..."
|
|
||||||
echo ", +" | sfdisk -q --no-reread -N 2 "$volume"
|
|
||||||
einfo "Updating kernel with new partition table..."
|
|
||||||
partx -u "$volume"
|
|
||||||
fi
|
|
||||||
einfo "Resizing..."
|
|
||||||
resize2fs "$mountpoint"
|
|
||||||
}
|
|
||||||
|
|
||||||
_lock_root_account() {
|
|
||||||
passwd -l root
|
|
||||||
}
|
|
||||||
|
|
||||||
_disable_password() {
|
|
||||||
echo "$1:*" | chpasswd -e
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
# Don't bootstrap if the host has already been bootstrapped
|
|
||||||
[ -f "/var/lib/cloud/.bootstrap-complete" ] && return 0
|
|
||||||
|
|
||||||
[ -d "/var/lib/cloud" ] || mkdir -p /var/lib/cloud
|
|
||||||
|
|
||||||
ebegin "Locking root account"; _lock_root_account; eend $?
|
|
||||||
ebegin "Disabling $EC2_USER password"; _disable_password "$EC2_USER"; eend $?
|
|
||||||
ebegin "Expanding root partition"; _resize_root_partition; eend $?
|
|
||||||
ebegin "Setting ec2 hostname"; _update_hostname; eend $?
|
|
||||||
ebegin "Setting ec2 user ssh keys"; _set_ssh_keys "$EC2_USER"; eend $?
|
|
||||||
ebegin "Running ec2 user data script"; _run_userdata; eend $?
|
|
||||||
|
|
||||||
touch "/var/lib/cloud/.bootstrap-complete"
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user