diff options
author | Mike Crute <mike@crute.us> | 2017-12-25 02:06:54 +0000 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2017-12-25 03:14:34 +0000 |
commit | 638be8d8b674acfb56bf59d586859a4622b9ad22 (patch) | |
tree | 22378879b6055d7c8f25030bc23e57906d9d46f4 | |
download | alpine-ec2-ami-638be8d8b674acfb56bf59d586859a4622b9ad22.tar.bz2 alpine-ec2-ami-638be8d8b674acfb56bf59d586859a4622b9ad22.tar.xz alpine-ec2-ami-638be8d8b674acfb56bf59d586859a4622b9ad22.zip |
Initial import
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | LICENSE.txt | 19 | ||||
-rw-r--r-- | Makefile | 22 | ||||
-rw-r--r-- | README.md | 72 | ||||
-rw-r--r-- | alpine-ami.yaml | 57 | ||||
-rwxr-xr-x | make_ami.sh | 325 | ||||
-rw-r--r-- | release.yaml | 21 |
7 files changed, 519 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98c621a --- /dev/null +++ b/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | /build/ | ||
2 | /.py3/ | ||
3 | /variables.json | ||
diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..736d3fe --- /dev/null +++ b/LICENSE.txt | |||
@@ -0,0 +1,19 @@ | |||
1 | Copyright (c) 2017 Michael Crute | ||
2 | |||
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
4 | this software and associated documentation files (the "Software"), to deal in | ||
5 | the Software without restriction, including without limitation the rights to | ||
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
7 | of the Software, and to permit persons to whom the Software is furnished to do | ||
8 | so, subject to the following conditions: | ||
9 | |||
10 | The above copyright notice and this permission notice shall be included in all | ||
11 | copies or substantial portions of the Software. | ||
12 | |||
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
19 | SOFTWARE. | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..de19494 --- /dev/null +++ b/Makefile | |||
@@ -0,0 +1,22 @@ | |||
1 | .PHONY: ami | ||
2 | ami: build/convert | ||
3 | build/convert alpine-ami.yaml > build/alpine-ami.json | ||
4 | packer build -var-file=variables.json build/alpine-ami.json | ||
5 | |||
6 | build/convert: | ||
7 | [ -d ".py3" ] || python3 -m venv .py3 | ||
8 | .py3/bin/pip install pyyaml | ||
9 | |||
10 | [ -d "build" ] || mkdir build | ||
11 | |||
12 | # Make stupid simple little YAML/JSON converter so we can maintain our | ||
13 | # packer configs in a sane format that allows comments but also use packer | ||
14 | # which only supports JSON | ||
15 | @echo "#!`pwd`/.py3/bin/python" > build/convert | ||
16 | @echo "import yaml, json, sys" >> build/convert | ||
17 | @echo "json.dump(yaml.load(open(sys.argv[1])), sys.stdout, indent=4, separators=(',', ': '))" >> build/convert | ||
18 | @chmod +x build/convert | ||
19 | |||
20 | .PHONY: clean | ||
21 | clean: | ||
22 | rm -rf build .py3 | ||
diff --git a/README.md b/README.md new file mode 100644 index 0000000..369ae45 --- /dev/null +++ b/README.md | |||
@@ -0,0 +1,72 @@ | |||
1 | # Alpine Linux EC2 AMI Build | ||
2 | |||
3 | **NOTE: This is not an official Amazon or AWS provided image. This is community | ||
4 | built and supported.** | ||
5 | |||
6 | This repository contains a packer file and a script to create an EC2 AMI | ||
7 | containing Alpine Linux. The AMI is designed to work with most EC2 features | ||
8 | natively and thus should launch on any instance type. If anything is missing | ||
9 | please report a bug. | ||
10 | |||
11 | This image can be launched on any modern instance type. Including T2, M5, C5, | ||
12 | I3, R4, P2, P3, X1, X1e, D2. Other instances may also work but have not been | ||
13 | tested. If you find an issue with instance support for any current generation | ||
14 | instance please file a bug against this project. | ||
15 | |||
16 | To get started use one of the AMIs below. The default user is `alpine` and will | ||
17 | be configured to use whatever SSH keys you chose when you launched the image. | ||
18 | If user data is specified it must be a shell script that begins with `#!`. If a | ||
19 | script is provided it will be executed as root after the network is configured. | ||
20 | |||
21 | **Note:** The AMI is not yet available in all regions. This file and | ||
22 | [releases.yaml](https://github.com/mcrute/alpine-ec2-ami/blob/master/releases.yaml) | ||
23 | will be updated as new regions are made available. | ||
24 | |||
25 | | Alpine Version | Region Code | Region Name | AMI ID | | ||
26 | | -------------- | -------------- | ------------------------- | --------------------------------------------------------------------------------------------- | | ||
27 | | 3.7 | us-east-1 | US East (N. Virginia) | [ami-XXXXXXXX](https://us-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
28 | | 3.7 | us-east-2 | US East (Ohio) | [ami-XXXXXXXX](https://us-east-2.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
29 | | 3.7 | us-west-1 | US West (N. California) | [ami-XXXXXXXX](https://us-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
30 | | 3.7 | us-west-2 | US West (Oregon) | [ami-032b877b](https://us-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-032b877b) | | ||
31 | | 3.7 | ca-central-1 | Canada (Central) | [ami-XXXXXXXX](https://ca-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
32 | | 3.7 | eu-central-1 | EU (Frankfurt) | [ami-XXXXXXXX](https://eu-central-1.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
33 | | 3.7 | eu-west-1 | EU (Ireland) | [ami-XXXXXXXX](https://eu-west-1.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
34 | | 3.7 | eu-west-2 | EU (London) | [ami-XXXXXXXX](https://eu-west-2.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
35 | | 3.7 | eu-west-3 | EU (Paris) | [ami-XXXXXXXX](https://eu-west-3.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
36 | | 3.7 | ap-northeast-1 | Asia Pacific (Tokyo) | [ami-XXXXXXXX](https://ap-northeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
37 | | 3.7 | ap-northeast-2 | Asia Pacific (Seoul) | [ami-XXXXXXXX](https://ap-northeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
38 | | 3.7 | ap-southeast-1 | Asia Pacific (Singapore) | [ami-XXXXXXXX](https://ap-southeast-1.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
39 | | 3.7 | ap-southeast-2 | Asia Pacific (Sydney) | [ami-XXXXXXXX](https://ap-southeast-2.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
40 | | 3.7 | ap-south-1 | Asia Pacific (Mumbai) | [ami-XXXXXXXX](https://ap-south-1.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
41 | | 3.7 | sa-east-1 | South America (São Paulo) | [ami-XXXXXXXX](https://sa-east-1.console.aws.amazon.com/ec2/home#launchAmi=ami-XXXXXXXX) | | ||
42 | |||
43 | ## Caveats | ||
44 | |||
45 | This image is being used in production but it's still somewhat early stage in | ||
46 | its development and thus there are some sharp edges. | ||
47 | |||
48 | - Only EBS-backed HVM instances are supported. While paravirtualized instances | ||
49 | are still available from AWS they are not supported on any of the newer | ||
50 | hardware so it seems unlikely that they will be supported going forward. Thus | ||
51 | this project does not support them. | ||
52 | |||
53 | - Not all packages required have been merged into the upstream aports tree. | ||
54 | When they are they will still only be available on edge. Until then the image | ||
55 | sources a few packages from a testing repo managed by the owner of this | ||
56 | repository. The builds in this repository should be identical to what is | ||
57 | eventually merged into the official tree. | ||
58 | |||
59 | - [cloud-init](https://cloudinit.readthedocs.io/en/latest/) is not currently | ||
60 | supported on Alpine Linux. Instead this image uses | ||
61 | [tiny-ec2-bootstrap](https://github.com/mcrute/tiny-ec2-bootstrap). Hostname | ||
62 | setting will work as will setting the ssh keys for the Alpine user based on | ||
63 | what was configured during instance launch. User data is supported as long | ||
64 | as it's a shell script (starts with #!). See the tiny-ec2-bootstrap README | ||
65 | for more details. You can still install cloud-init using aports but the | ||
66 | version in the tree is somewhat old and may not work correctly for Alpine. | ||
67 | If full cloud-init support is important to you please file a bug against this | ||
68 | project. | ||
69 | |||
70 | - CloudFormation support is still forthcoming. This requires patches and | ||
71 | packaging for the upstream cfn tools that have not yet been accepted. | ||
72 | Eventually full CloudFormation support will be available. | ||
diff --git a/alpine-ami.yaml b/alpine-ami.yaml new file mode 100644 index 0000000..26b75b1 --- /dev/null +++ b/alpine-ami.yaml | |||
@@ -0,0 +1,57 @@ | |||
1 | variables: | ||
2 | security_group: "" | ||
3 | subnet: "" | ||
4 | destination_regions: "" | ||
5 | alpine_release: "3.7" | ||
6 | |||
7 | # Don't override this without a good reason and if you do just make sure it | ||
8 | # gets passed all the way through to the make_ami script | ||
9 | volume_name: "/dev/xvdf" | ||
10 | |||
11 | builders: | ||
12 | - type: "amazon-ebssurrogate" | ||
13 | |||
14 | # Image is built inside a custom VPC so let Packer use the existing | ||
15 | # resources | ||
16 | security_group_id: "{{user `security_group`}}" | ||
17 | subnet_id: "{{user `subnet`}}" | ||
18 | |||
19 | # Input Instance Setting | ||
20 | instance_type: "t2.micro" | ||
21 | launch_block_device_mappings: | ||
22 | - volume_type: "gp2" | ||
23 | device_name: "{{user `volume_name`}}" | ||
24 | delete_on_termination: false | ||
25 | volume_size: 5 | ||
26 | |||
27 | # Output AMI Settings | ||
28 | ena_support: true | ||
29 | ami_name: "Alpine-{{user `alpine_release`}}-Hardened-EC2" | ||
30 | ami_description: "Alpine Linux {{user `alpine_release`}} Release with Hardened Kernel and EC2 Optimizations" | ||
31 | ami_groups: | ||
32 | - "all" | ||
33 | ami_virtualization_type: "hvm" | ||
34 | ami_regions: "{{user `destination_regions`}}" | ||
35 | ami_root_device: | ||
36 | source_device_name: "{{user `volume_name`}}" | ||
37 | device_name: "/dev/xvda" | ||
38 | delete_on_termination: true | ||
39 | volume_size: 5 | ||
40 | volume_type: "gp2" | ||
41 | |||
42 | # Use the most recent Amazon Linux AMI as our base | ||
43 | ssh_username: "ec2-user" | ||
44 | source_ami_filter: | ||
45 | filters: | ||
46 | virtualization-type: "hvm" | ||
47 | root-device-type: "ebs" | ||
48 | architecture: "x86_64" | ||
49 | name: "amzn-ami-hvm-*-x86_64-gp2" | ||
50 | owners: | ||
51 | - "137112412989" | ||
52 | most_recent: true | ||
53 | |||
54 | provisioners: | ||
55 | - type: "shell" | ||
56 | script: "make_ami.sh" | ||
57 | execute_command: "sudo sh -c '{{ .Vars }} {{ .Path }} {{user `volume_name`}}'" | ||
diff --git a/make_ami.sh b/make_ami.sh new file mode 100755 index 0000000..6ad4b0d --- /dev/null +++ b/make_ami.sh | |||
@@ -0,0 +1,325 @@ | |||
1 | #!/bin/sh | ||
2 | # vim:set ts=4: | ||
3 | |||
4 | set -eu | ||
5 | |||
6 | : ${ALPINE_RELEASE:="3.7"} # not tested against edge | ||
7 | : ${APK_TOOLS_URI:="https://github.com/alpinelinux/apk-tools/releases/download/v2.8.0/apk-tools-2.8.0-x86_64-linux.tar.gz"} | ||
8 | : ${APK_TOOLS_SHA256:="da21cefd2121e3a6cd4e8742b38118b2a1132aad7f707646ee946a6b32ee6df9"} | ||
9 | : ${ALPINE_KEYS:="http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/alpine-keys-2.1-r1.apk"} | ||
10 | : ${ALPINE_KEYS_SHA256:="7b2d1e9a00324c8eee49785dc22355be02534201e77473ba9762027e1a475cc7"} | ||
11 | |||
12 | die() { | ||
13 | printf '\033[1;31mERROR:\033[0m %s\n' "$@" >&2 # bold red | ||
14 | exit 1 | ||
15 | } | ||
16 | |||
17 | einfo() { | ||
18 | printf '\n\033[1;36m> %s\033[0m\n' "$@" >&2 # bold cyan | ||
19 | } | ||
20 | |||
21 | rc_add() { | ||
22 | local target="$1"; shift # target directory | ||
23 | local runlevel="$1"; shift # runlevel name | ||
24 | local services="$*" # names of services | ||
25 | |||
26 | local svc; for svc in $services; do | ||
27 | mkdir -p "$target"/etc/runlevels/$runlevel | ||
28 | ln -s /etc/init.d/$svc "$target"/etc/runlevels/$runlevel/$svc | ||
29 | echo " * service $svc added to runlevel $runlevel" | ||
30 | done | ||
31 | } | ||
32 | |||
33 | wgets() ( | ||
34 | local url="$1" # url to fetch | ||
35 | local sha256="$2" # expected SHA256 sum of output | ||
36 | local dest="$3" # output path and filename | ||
37 | |||
38 | wget -T 10 -q -O "$dest" "$url" | ||
39 | echo "$sha256 $dest" | sha256sum -c > /dev/null | ||
40 | ) | ||
41 | |||
42 | |||
43 | validate_block_device() { | ||
44 | local dev="$1" # target directory | ||
45 | |||
46 | lsblk -P --fs "$dev" >/dev/null 2>&1 || \ | ||
47 | die "'$dev' is not a valid block device" | ||
48 | |||
49 | if lsblk -P --fs "$dev" | grep -vq 'FSTYPE=""'; then | ||
50 | die "Block device '$dev' is not blank" | ||
51 | fi | ||
52 | } | ||
53 | |||
54 | fetch_apk_tools() { | ||
55 | local store="$(mktemp -d)" | ||
56 | local tarball="$(basename $APK_TOOLS_URI)" | ||
57 | |||
58 | wgets "$APK_TOOLS_URI" "$APK_TOOLS_SHA256" "$store/$tarball" | ||
59 | tar -C "$store" -xf "$store/$tarball" | ||
60 | |||
61 | find "$store" -name apk | ||
62 | } | ||
63 | |||
64 | make_filesystem() { | ||
65 | local device="$1" # target device path | ||
66 | local target="$2" # mount target | ||
67 | |||
68 | mkfs.ext4 "$device" | ||
69 | e2label "$device" / | ||
70 | mount "$device" "$target" | ||
71 | } | ||
72 | |||
73 | setup_repositories() { | ||
74 | local target="$1" # target directory | ||
75 | |||
76 | mkdir -p "$target"/etc/apk/keys | ||
77 | cat > "$target"/etc/apk/repositories <<-EOF | ||
78 | http://dl-cdn.alpinelinux.org/alpine/v$ALPINE_RELEASE/main | ||
79 | http://dl-cdn.alpinelinux.org/alpine/v$ALPINE_RELEASE/community | ||
80 | EOF | ||
81 | } | ||
82 | |||
83 | # This is mostly a temporary measure because some required packages have not | ||
84 | # yet been accepted upstream. This can be removed when the following pull | ||
85 | # requests are merged: | ||
86 | # | ||
87 | # - https://github.com/alpinelinux/aports/pull/2962 | ||
88 | # - https://github.com/alpinelinux/aports/pull/2961 | ||
89 | setup_staging_repos() { | ||
90 | local target="$1" # target directory | ||
91 | |||
92 | echo "https://mcrute-build-artifacts.s3.us-west-2.amazonaws.com/alpine-packages/$ALPINE_RELEASE/testing" >> "$target"/etc/apk/repositories | ||
93 | |||
94 | cat > "$target"/etc/apk/keys/mcrute-5a3eecec.rsa.pub <<-EOF | ||
95 | -----BEGIN PUBLIC KEY----- | ||
96 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5fW5dyTqgs9Yf93xKn5U | ||
97 | cYzY9t//M3TAaiDWH7rFxqBqTGnVGkP9QAGqsbXyoo/JpIalazkOfm/1L+XaK7NI | ||
98 | IUD/8KxfrnBW53cc/KOkPcGAga36aTBz/HmLQQvjWcizPxWepjdfvAnRTMV69Oud | ||
99 | zaRPGKx8nCRqLy1YFAEXn+zpHRh+OHCzzQFlkJop+2PCXqDFaMWC7+oWwrqFs1i0 | ||
100 | CXc4pq5oT6vAQyt6pUwN85sLVxtxXSt5G5ALYzQtaIj7IAR3jGlwU26wOAv5YP7z | ||
101 | xn/Z1ebQsPbAl3rw48v2T2ohPEX2TUtUq4OuwOG+z1pi3woIGOlOFVAP3k6lm8Z9 | ||
102 | 9QIDAQAB | ||
103 | -----END PUBLIC KEY----- | ||
104 | EOF | ||
105 | } | ||
106 | |||
107 | fetch_keys() { | ||
108 | local target="$1" | ||
109 | local tmp="$(mktemp -d)" | ||
110 | |||
111 | wgets "$ALPINE_KEYS" "$ALPINE_KEYS_SHA256" "$tmp/alpine-keys.apk" | ||
112 | tar -C "$target" -xvf "$tmp"/alpine-keys.apk etc/apk/keys | ||
113 | rm -rf "$tmp" | ||
114 | } | ||
115 | |||
116 | setup_chroot() { | ||
117 | local target="$1" | ||
118 | |||
119 | mount -t proc none "$target"/proc | ||
120 | mount --bind /dev "$target"/dev | ||
121 | mount --bind /sys "$target"/sys | ||
122 | |||
123 | # Don't want to ship this but it's needed for bootstrap. Will be removed in | ||
124 | # the cleanup stage. | ||
125 | install -Dm644 /etc/resolv.conf "$target"/etc/resolv.conf | ||
126 | } | ||
127 | |||
128 | install_core_packages() { | ||
129 | local target="$1" | ||
130 | |||
131 | # Most from: https://git.alpinelinux.org/cgit/alpine-iso/tree/alpine-virt.packages | ||
132 | # | ||
133 | # acct - installed by some configurations, so added here | ||
134 | # aws-ena-driver-hardened - required for ENA enabled instances | ||
135 | # e2fsprogs - required by init scripts to maintain ext4 volumes | ||
136 | # linux-hardened - can't use virthardened because it's missing NVME support | ||
137 | # mkinitfs - required to build custom initfs | ||
138 | # sudo - to allow alpine user to become root, disallow root SSH logins | ||
139 | # tiny-ec2-bootstrap - to bootstrap system from EC2 metadata | ||
140 | chroot "$target" apk --no-cache add \ | ||
141 | acct \ | ||
142 | alpine-mirrors \ | ||
143 | aws-ena-driver-hardened \ | ||
144 | chrony \ | ||
145 | e2fsprogs \ | ||
146 | linux-hardened \ | ||
147 | mkinitfs \ | ||
148 | openssh \ | ||
149 | sudo \ | ||
150 | tiny-ec2-bootstrap \ | ||
151 | tzdata | ||
152 | |||
153 | chroot "$target" apk --no-cache add --no-scripts syslinux | ||
154 | } | ||
155 | |||
156 | create_initfs() { | ||
157 | local target="$1" | ||
158 | |||
159 | # Create ENA feature for mkinitfs | ||
160 | # Submitted upstream: https://github.com/alpinelinux/mkinitfs/pull/19 | ||
161 | echo "kernel/drivers/net/ethernet/amazon" > \ | ||
162 | "$target"/etc/mkinitfs/features.d/ena.modules | ||
163 | |||
164 | # Enable ENA and NVME features these don't hurt for any instance and are | ||
165 | # hard requirements of the 5 series and i3 series of instances | ||
166 | sed -Ei 's/^features="([^"]+)"/features="\1 nvme ena"/' \ | ||
167 | "$target"/etc/mkinitfs/mkinitfs.conf | ||
168 | |||
169 | chroot "$target" /sbin/mkinitfs $(basename $(find "$target"/lib/modules/* -maxdepth 0)) | ||
170 | } | ||
171 | |||
172 | setup_extlinux() { | ||
173 | local target="$1" | ||
174 | |||
175 | # Must use disk labels instead of UUID or devices paths so that this works | ||
176 | # across instance familes. UUID works for many instances but breaks on the | ||
177 | # NVME ones because EBS volumes are hidden behind NVME devices. | ||
178 | # | ||
179 | # Enable ext4 because the root device is formatted ext4 | ||
180 | # | ||
181 | # Shorten timeout because EC2 has no way to interact with instance console | ||
182 | sed -Ei -e "s|^[# ]*(root)=.*|\1=LABEL=/|" \ | ||
183 | -e "s|^[# ]*(default_kernel_opts)=.*|\1=|" \ | ||
184 | -e "s|^[# ]*(modules)=.*|\1=sd-mod,usb-storage,ext4|" \ | ||
185 | -e "s|^[# ]*(default)=.*|\1=hardened|" \ | ||
186 | -e "s|^[# ]*(timeout)=.*|\1=1|" \ | ||
187 | "$target"/etc/update-extlinux.conf | ||
188 | } | ||
189 | |||
190 | install_extlinux() { | ||
191 | local target="$1" | ||
192 | |||
193 | chroot "$target" /sbin/extlinux --install /boot | ||
194 | chroot "$target" /sbin/update-extlinux --warn-only | ||
195 | } | ||
196 | |||
197 | setup_fstab() { | ||
198 | local target="$1" | ||
199 | |||
200 | cat > "$target"/etc/fstab <<-EOF | ||
201 | # <fs> <mountpoint> <type> <opts> <dump/pass> | ||
202 | LABEL=/ / ext4 defaults,noatime 1 1 | ||
203 | EOF | ||
204 | } | ||
205 | |||
206 | setup_networking() { | ||
207 | local target="$1" | ||
208 | |||
209 | cat > "$target"/etc/network/interfaces <<-EOF | ||
210 | auto lo | ||
211 | iface lo inet loopback | ||
212 | |||
213 | auto eth0 | ||
214 | iface eth0 inet dhcp | ||
215 | EOF | ||
216 | } | ||
217 | |||
218 | enable_services() { | ||
219 | local target="$1" | ||
220 | |||
221 | rc_add "$target" default sshd chronyd networking tiny-ec2-bootstrap | ||
222 | rc_add "$target" sysinit devfs dmesg mdev hwdrivers | ||
223 | rc_add "$target" boot modules hwclock swap hostname sysctl bootmisc syslog | ||
224 | rc_add "$target" shutdown killprocs savecache mount-ro | ||
225 | } | ||
226 | |||
227 | create_alpine_user() { | ||
228 | local target="$1" | ||
229 | |||
230 | # Allow members of the wheel group to sudo without a password. By default | ||
231 | # this will only be the alpine user. This allows us to ship an AMI that is | ||
232 | # accessible via SSH using the user's configured SSH keys (thanks to | ||
233 | # tiny-ec2-bootstrap) but does not allow remote root access which is the | ||
234 | # best-practice. | ||
235 | sed -i '/%wheel .* NOPASSWD: .*/s/^# //' "$target"/etc/sudoers | ||
236 | |||
237 | # There is no real standard ec2 username across AMIs, Amazon uses ec2-user | ||
238 | # for their Amazon Linux AMIs but Ubuntu uses ubuntu, Fedora uses fedora, | ||
239 | # etc... (see: https://alestic.com/2014/01/ec2-ssh-username/). So our user | ||
240 | # and group are alpine because this is Alpine Linux. On instance bootstrap | ||
241 | # the user can create whatever users they want and delete this one. | ||
242 | chroot "$target" /usr/sbin/addgroup alpine | ||
243 | chroot "$target" /usr/sbin/adduser -h /home/alpine -s /bin/sh -G alpine -D alpine | ||
244 | chroot "$target" /usr/sbin/addgroup alpine wheel | ||
245 | chroot "$target" /usr/bin/passwd -u alpine | ||
246 | } | ||
247 | |||
248 | configure_ntp() { | ||
249 | local target="$1" | ||
250 | |||
251 | # EC2 provides an instance-local NTP service syncronized with GPS and | ||
252 | # atomic clocks in-region. Prefer this over external NTP hosts when running | ||
253 | # in EC2. | ||
254 | # | ||
255 | # See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html | ||
256 | sed -i 's/^server .*/server 169.254.169.123/' "$target"/etc/chrony/chrony.conf | ||
257 | } | ||
258 | |||
259 | cleanup() { | ||
260 | local target="$1" | ||
261 | |||
262 | # Sweep cruft out of the image that doesn't need to ship or will be | ||
263 | # re-generated when the image boots | ||
264 | rm -f \ | ||
265 | "$target"/var/cache/apk/* \ | ||
266 | "$target"/etc/resolv.conf \ | ||
267 | "$target"/root/.ash_history \ | ||
268 | "$target"/etc/*- | ||
269 | |||
270 | umount \ | ||
271 | "$target"/dev \ | ||
272 | "$target"/proc \ | ||
273 | "$target"/sys | ||
274 | |||
275 | umount "$target" | ||
276 | } | ||
277 | |||
278 | main() { | ||
279 | [ "$#" -ne 1 ] && { echo "usage: $0 <block-device>"; exit 1; } | ||
280 | |||
281 | device="$1" | ||
282 | target="/mnt/target" | ||
283 | |||
284 | validate_block_device "$device" | ||
285 | |||
286 | [ -d "$target" ] || mkdir "$target" | ||
287 | |||
288 | einfo "Fetching static APK tools" | ||
289 | apk="$(fetch_apk_tools)" | ||
290 | |||
291 | einfo "Creating root filesystem" | ||
292 | make_filesystem "$device" "$target" | ||
293 | |||
294 | setup_repositories "$target" | ||
295 | |||
296 | einfo "Fetching Alpine signing keys" | ||
297 | fetch_keys "$target" | ||
298 | |||
299 | setup_staging_repos "$target" | ||
300 | |||
301 | einfo "Installing base system" | ||
302 | $apk add --root "$target" --update-cache --initdb alpine-base | ||
303 | |||
304 | setup_chroot "$target" | ||
305 | |||
306 | einfo "Installing core packages" | ||
307 | install_core_packages "$target" | ||
308 | |||
309 | einfo "Configuring and enabling boot loader" | ||
310 | create_initfs "$target" | ||
311 | setup_extlinux "$target" | ||
312 | install_extlinux "$target" | ||
313 | |||
314 | einfo "Configuring system" | ||
315 | setup_fstab "$target" | ||
316 | setup_networking "$target" | ||
317 | enable_services "$target" | ||
318 | create_alpine_user "$target" | ||
319 | configure_ntp "$target" | ||
320 | |||
321 | einfo "All done, cleaning up" | ||
322 | cleanup "$target" | ||
323 | } | ||
324 | |||
325 | main "$@" | ||
diff --git a/release.yaml b/release.yaml new file mode 100644 index 0000000..66d80c7 --- /dev/null +++ b/release.yaml | |||
@@ -0,0 +1,21 @@ | |||
1 | Alpine-3.7-Hardened-EC2: | ||
2 | description: "Alpine Linux 3.7 Release with Hardened Kernel and EC2 Optimizations" | ||
3 | alpine-release: 3.7 | ||
4 | kernel-flavor: hardened | ||
5 | ami-release-date: "2017-12-25 03:02:00" | ||
6 | region-identifiers: | ||
7 | #us-east-1: ami-XXXXXXXX | ||
8 | #us-east-2: ami-XXXXXXXX | ||
9 | #us-west-1: ami-XXXXXXXX | ||
10 | us-west-2: ami-032b877b | ||
11 | #ca-central-1: ami-XXXXXXXX | ||
12 | #eu-central-1: ami-XXXXXXXX | ||
13 | #eu-west-1: ami-XXXXXXXX | ||
14 | #eu-west-2: ami-XXXXXXXX | ||
15 | #eu-west-3: ami-XXXXXXXX | ||
16 | #ap-northeast-1: ami-XXXXXXXX | ||
17 | #ap-northeast-2: ami-XXXXXXXX | ||
18 | #ap-southeast-1: ami-XXXXXXXX | ||
19 | #ap-southeast-2: ami-XXXXXXXX | ||
20 | #ap-south-1: ami-XXXXXXXX | ||
21 | #sa-east-1: ami-XXXXXXXX | ||