Skip to content

Commit

Permalink
[v13] Backport hardened AMI resources (#27454)
Browse files Browse the repository at this point in the history
* Packer resources for hardened AMIs (#26126)

- Converts the original JSON Packer template to HCL, preserving the
   old json version for the legacy AMI builds
 - Updates and modernises the new HCL template
 - Creates a slimmed-down `install-hardened.sh` script that avoids
   installing the monitoring front-end tools, as these are a major
   source of CIS violations (e.g. pulling in X11)
 - Splits the exisiting resources in `files` into two classes:
   - `files` that is common to the old and new AMI, and
   - `monitor-files` that is only installed on the legacy AMI

Note that this patch does not include any high-level tools to drive this
packer script from CI/CD. That is coming in a later PR.

* Creates Hardened AMI promotion tool (#26754)

* Creates Hardened AMI promotion tool

Creates a tool that handles AMI promotion by finding the latest AMI
for a given Teleport release and makks it as public.

This patch also moves the `update-ami-id` script into its own package
directory in the `assets/aws` module, in order to both highlight its
use and tidy up the module root.

* Apply suggestions from code review

Co-authored-by: Noah Stride <noah.stride@goteleport.com>

* Update main.go

---------

Co-authored-by: Noah Stride <noah.stride@goteleport.com>

---------

Co-authored-by: Noah Stride <noah.stride@goteleport.com>
  • Loading branch information
tcsc and strideynet committed Jun 7, 2023
1 parent 13e8e57 commit a07db7f
Show file tree
Hide file tree
Showing 8 changed files with 457 additions and 3 deletions.
6 changes: 3 additions & 3 deletions assets/aws/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@ check-vars:
.PHONY: update-ami-ids-terraform
update-ami-ids-terraform:
@echo -e "\nUpdating OSS Terraform image IDs"
go run main.go --aws-account $(AWS_ACCOUNT_ID) --regions $(DESTINATION_REGIONS) --version $(TELEPORT_VERSION) --type oss
go run ./cmd/update-ami-id --aws-account $(AWS_ACCOUNT_ID) --regions $(DESTINATION_REGIONS) --version $(TELEPORT_VERSION) --type oss
@echo -e "\nUpdating Enterprise Terraform image IDs"
go run main.go --aws-account $(AWS_ACCOUNT_ID) --regions $(DESTINATION_REGIONS) --version $(TELEPORT_VERSION) --type ent
go run ./cmd/update-ami-id --aws-account $(AWS_ACCOUNT_ID) --regions $(DESTINATION_REGIONS) --version $(TELEPORT_VERSION) --type ent
@echo -e "\nUpdating Enterprise FIPS Terraform image IDs"
go run main.go --aws-account $(AWS_ACCOUNT_ID) --regions $(DESTINATION_REGIONS) --version $(TELEPORT_VERSION) --type ent-fips
go run ./cmd/update-ami-id --aws-account $(AWS_ACCOUNT_ID) --regions $(DESTINATION_REGIONS) --version $(TELEPORT_VERSION) --type ent-fips

# you will need the Github 'gh' CLI installed and working to be able to use this target
# https://github.com/cli/cli/releases/latest
Expand Down
159 changes: 159 additions & 0 deletions assets/aws/cmd/make-public/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
Copyright 2023 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// This package implements atool that finds all of the latest AMIs for a
// release and ensures that they are public.

package main

import (
"context"
"errors"
"fmt"
"log"
"strings"
"time"

"github.com/alecthomas/kingpin/v2"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
)

type Args struct {
accountId string
teleportVersion string
regions []string
}

func parseArgs() Args {
args := Args{}

kingpin.Flag("aws-account", "AWS Account ID").
Required().
StringVar(&args.accountId)

kingpin.Flag("teleport-version", "Version of teleport AMIs to make public").
Required().
StringVar(&args.teleportVersion)

var regions string
kingpin.Flag("regions", "A comma-separated list of AWS regions to update").
Required().
StringVar(&regions)

kingpin.Parse()

args.regions = strings.Split(regions, ",")

return args
}

func main() {
args := parseArgs()

ctx := context.Background()

for _, region := range args.regions {
cfg, err := config.LoadDefaultConfig(ctx,
config.WithRegion(region))
if err != nil {
log.Fatalf("could not load AWS config for %s: %v", region, err)
}

client := ec2.NewFromConfig(cfg)

for _, edition := range []string{"oss", "ent"} {
for _, fips := range []string{"false", "true"} {
// No such combination exists
if edition == "oss" && fips == "true" {
continue
}

ami, err := findLatestAMI(ctx, client, args.accountId, args.teleportVersion, edition, fips)
switch {
case err == nil:
break

case errors.Is(err, notFound):
continue

default:
log.Fatalf("Failed to find the latest AMI: %s", err)
}

// Mark the AMI as public
log.Printf("Marking %s as public", ami)
_, err = client.ModifyImageAttribute(ctx, &ec2.ModifyImageAttributeInput{
ImageId: aws.String(ami),
Attribute: aws.String("launchPermission"),
LaunchPermission: &types.LaunchPermissionModifications{
Add: []types.LaunchPermission{
{Group: types.PermissionGroupAll},
},
},
})
if err != nil {
log.Printf("WARNING: Failed to make ami %q public: %s", ami, err)
continue
}
}
}
}
}

var notFound error = fmt.Errorf("not found")

func findLatestAMI(ctx context.Context, client *ec2.Client, accountId, teleportVersion, edition, fips string) (string, error) {
resp, err := client.DescribeImages(ctx, &ec2.DescribeImagesInput{
Filters: []types.Filter{
{Name: aws.String("name"), Values: []string{"teleport-*"}},
{Name: aws.String("tag:TeleportVersion"), Values: []string{teleportVersion}},
{Name: aws.String("tag:TeleportEdition"), Values: []string{edition}},
{Name: aws.String("tag:TeleportFipsEnabled"), Values: []string{fips}},
{Name: aws.String("tag:BuildType"), Values: []string{"production"}},
},
Owners: []string{accountId},
})

if err != nil {
return "", fmt.Errorf("querying AMIs: %w", err)
}

if len(resp.Images) == 0 {
return "", notFound
}

// I'm assuming that we will have few enough images returned in
// any given search that it is not worth setting up a fancy sorting
// predicate
newestTimestamp := time.Time{}
newestAMI := -1
for i := range resp.Images {
creationDate, err := time.Parse(time.RFC3339, *resp.Images[i].CreationDate)
if err != nil {
return "", fmt.Errorf("parsing timestamp: %w", err)
}

if creationDate.After(newestTimestamp) {
newestTimestamp = creationDate
newestAMI = i
}
}

return *resp.Images[newestAMI].ImageId, nil
}
File renamed without changes.
84 changes: 84 additions & 0 deletions assets/aws/files/install-hardened.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/bin/bash

# Set some curl options so that temporary failures get retried
# More info: https://ec.haxx.se/usingcurl-timeouts.html
CURL_OPTS=(-L --retry 100 --retry-delay 0 --connect-timeout 10 --max-time 300)

# Update packages
dnf -y update

# Install
# - uuid used for random token generation,
# - python for certbot
dnf install -y uuid python3

# Install certbot
python3 -m venv /opt/certbot
/opt/certbot/bin/pip install --upgrade pip
/opt/certbot/bin/pip install certbot certbot-dns-route53
ln -s /opt/certbot/bin/certbot /usr/bin/certbot

# Create teleport user. It is helpful to share the same UID
# to have the same permissions on shared NFS volumes across auth servers and for consistency.
useradd -r teleport -u "${TELEPORT_UID}" -d /var/lib/teleport
# Add teleport to adm group to read and write logs
usermod -a -G adm teleport

# Setup teleport run dir for pid files
install -d -m 0700 -o teleport -g adm /var/lib/teleport
install -d -m 0755 -o teleport -g adm /run/teleport /etc/teleport.d


# Pick the teleport tarball filename matching the requested teleport
# edition.
case "${TELEPORT_TYPE}-${TELEPORT_FIPS}" in
oss-0) TARBALL="teleport-v${TELEPORT_VERSION}-linux-amd64-bin.tar.gz" ;;
ent-0) TARBALL="teleport-ent-v${TELEPORT_VERSION}-linux-amd64-bin.tar.gz" ;;
ent-1) TARBALL="teleport-ent-v${TELEPORT_VERSION}-linux-amd64-fips-bin.tar.gz" ;;
oss-1)
echo "OSS FIPS not supported" >&2
exit 1
;;
*)
echo "Invalid environment" >&2
exit 1
;;
esac
TARBALL_FILENAME="/tmp/files/${TARBALL}"

if [[ -f "${TARBALL_FILENAME}" ]]; then
echo "Found locally uploaded tarball: ${TARBALL_FILENAME}, moving to /tmp/teleport.tar.gz"
mv "${TARBALL_FILENAME}" /tmp/teleport.tar.gz
else
echo "Downloading teleport tarball ${TARBALL}"
curl "${CURL_OPTS[@]}" -o /tmp/teleport.tar.gz "https://get.gravitational.com/teleport/${TELEPORT_VERSION}/${TARBALL}"
fi

# Extract tarball to /tmp/teleport to get the binaries out
mkdir /tmp/teleport
tar -C /tmp/teleport -x -z -f /tmp/teleport.tar.gz --strip-components=1
install -m 755 /tmp/teleport/{tctl,tsh,teleport,tbot} /usr/local/bin
rm -rf /tmp/teleport /tmp/teleport.tar.gz

if [[ "${TELEPORT_FIPS}" == 1 ]]; then
# add --fips to 'teleport start' commands in FIPS mode
sed -i -E 's_^(ExecStart=/usr/local/bin/teleport start)_\1 --fips_' /etc/systemd/system/teleport*.service
fi

# Add /usr/local/bin to path used by sudo (so 'sudo tctl users add' will work as per the docs)
echo "Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin" > /etc/sudoers.d/secure_path

# Clean up the authorized keys not used
rm -f /root/.ssh/authorized_keys
rm -f /home/ec2-user/.ssh/authorized_keys

# Clean up copied temp files
rm -rf /tmp/files

# Clean up all packages
dnf -y clean all
rm -rf /var/cache/dnf /var/cache/yum

# Enable Teleport services to start on boot
systemctl enable teleport-generate-config.service
systemctl enable teleport.service
File renamed without changes.
4 changes: 4 additions & 0 deletions assets/aws/single-ami.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@
"type": "file",
"source": "files/",
"destination": "/tmp/files"
},{
"type": "file",
"source": "monitor-files/",
"destination": "/tmp/files"
},{
"inline": [
"sudo cp /tmp/files/system/* /etc/systemd/system/",
Expand Down

0 comments on commit a07db7f

Please sign in to comment.