Web S3 Docker

A Terraform module, creating a self-hosted static web hosting solution in the Hetzner cloud, offering S3 object storage and a Docker registry.

  • Public and private S3 buckets
  • Custom domains for public S3 Buckets
  • Public and private Docker Registry

Prerequisites

The module will create DNS entries for the various service endpoints. This requires a valid DNS zone that is managed via the Hetzner Cloud API.

Usage Example

resource "tls_private_key" "ssh_key" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "hcloud_ssh_key" "ssh_key1" {
  name       = "ssh-key1"
  public_key = tls_private_key.ssh_key.public_key_openssh
}

module "web-s3-docker" {
  source   = "https://github.com/pellepelster/solidblocks/releases/download/v0.4.12/blcks-terraform-hcloud-web-s3-docker-v0.4.12.zip"
  name     = "server1"
  dns_zone = "blcks-test.de"

  ssh_keys = [hcloud_ssh_key.ssh_key1.id]

  s3_buckets = [
    {
      name                     = "bucket1",
      web_access_public_enable = true,
      web_access_domains       = ["blcks-test.de", "www.blcks-test.de"]
    },
    {
      name             = "bucket2",
      owner_key_id     = "cbeebb7f1fa4de50025a5c95",
      owner_secret_key = "f775d527e821ab035b5a874e6326f20c135b2d2fc903112fe768a17681f54043"
    },
  ]

  disable_volume_delete_protection = true
}

Configuration

Name and DNS

The name and dns_zone will be used as base domain for all created endpoints using the pattern <name>.<dns_zone>.

S3 Buckets

The variable s3_buckets can be used to auto-create S3 buckets during provisioning.

Minimal Bucket configuration

module "web_s3_docker" {
  # ...
  s3_buckets = [{ name = "bucket1" }]
}

will create a single S3 bucket named bucket1 with autogenerated access keys for the bucket owner, a read-write rw as well as a read-only ro user. The keys can be retrieved using the module output s3_buckets.

Example output for s3_buckets

[
  {
    "name": "bucket1",
    "owner_key_id": "GKxxxxxxxxxxxxxxxxxxxxxxxx",
    "owner_secret_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "ro_key_id": "GKxxxxxxxxxxxxxxxxxxxxxxxx",
    "ro_secret_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "rw_key_id": "GKxxxxxxxxxxxxxxxxxxxxxxxx",
    "rw_secret_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    //...
  }
]

Together with the base S3 api address from the output s3_host the bucket can be used with S3 tools like s3cmd like this

export S3_HOST=$(terraform output -raw s3_host)
export ACCESS_KEY=$(terraform output -raw s3_host | jq '.[0].owner_key_id')
export SECRET_KEY=$(terraform output -raw s3_host | jq '.[0].owner_secret_key')

s3cmd --host-bucket ${S3_HOST} --host ${S3_HOST} --access_key ${ACCESS_KEY} --secret_key ${SECRET_KEY} ls s3://bucket1

Custom access keys

You can override the access key ids as well as the secrets by providing your own. To do this simply provide them in the bucket config

module "web_s3_docker" {
  //...
  s3_buckets = [
    {
      name             = "bucket2",
      owner_key_id     = "cbeebb7f1fa4de50025a5c95",
      owner_secret_key = "f775d527e821ab035b5a874e6326f20c135b2d2fc903112fe768a17681f54043"
    },
  ]
}

[owner|rw|ro]_key_id has to be 24 characters long, and [owner|rw|ro]_secret_key 64. You can either provide both, just one or none, the missing parts will be automatically generated. The module takes care to add the GK prefix which is needed by the underlying S3 server.

Static web hosting

By setting the bucket configuration web_access_public_enable to true, static web hosting can be enabled for the Bucket, making all its content available on the internet. The list web_access_domains enables access to this bucket using custom domains.

Tip

DNS entries for all domains that are within the dns_zone are auto-created. If the provided web_access_domains is not part of the dns_zone the A respectively AAAA DNS entries need to be created manually, the IPv4 and IPv6 addresses are available through the module output ipv4_address and ipv6_address.

The created web endpoints again can be retrieved via the s3_buckets output.

Example output for a bucket with web_access_public_enable

[
  {
    "name": "bucket1",
    //...
    "web_access_public_enable": true,
    "web_access_addresses": [
      "https://bucket1.s3-web.web-s3-docker.blcks-test.de",
      "https://blcks-test.de",
      "https://www.blcks-test.de"
    ]
  }
]
Tip

When uploading content to be served it is important to use the correct mime-types, when using s3cmd to upload content this can be achieved like this

s3cmd sync --no-mime-magic --guess-mime-type ./contnt/* s3://<bucket_name>

Docker

By setting docker_enable to true the service will also expose a docker registry. The creation of credentials follows the pattern of the S3 bucket access key creation. If no user is specified a read-write as well as a read-only user will be autogenerated. Those can be retrieved via the outputs docker_rw_users respectively docker_ro_users.

Example output for docker_rw_users

[
  {
    "username": "61008df60fd0abac80a5cba645056a80",
    "password": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
]

using the private docker endpoint that can be retrieved from docker_host_private, docker images can be pushed like this

export DOCKER_HOST_PRIVATE=$(terraform output -raw docker_host_private)
export DOCKER_USERNAME=$(terraform output -raw docker_rw_users | jq '.[0].username')
export DOCKER_PASSWORD=$(terraform output -raw docker_rw_users | jq '.[0].password')

echo ${DOCKER_PASSWORD} | docker login ${DOCKER_HOST_PRIVATE} --username ${DOCKER_USERNAME} --password-stdin
docker push ${DOCKER_HOST_PRIVATE}/my-image:latest

Public registry

When docker_public_enable is set to true all images that are pushed to the private endpoint (docker_host_private) cn be pulled without authentication from the public endpoint

export DOCKER_HOST_PUBLIC=$(terraform output -raw docker_host_public)

docker pull ${DOCKER_HOST_PUBLIC}/my-image:latest

Endpoints

endpointdescriptionaddress
S3S3 api endpoint<bucket>.s3.<name>.<dns_zone>
S3 Webpublic accessible endpoint if web_access_public_enable is set to true<bucket>.s3-web.<name>.<dns_zone>
S3 Admin APIAPI endpoint for the GarageFS S3 admin API. Adress can be as well as the access token can be retrieved from the output garage_admin_address and garage_admin_tokens3-admin.<name>.<dns_zone>
private docker registryendpoint for publishing and pulling Docker images usein credentialsdocker-private.s3-web.<name>.<dns_zone>
public docker registryendpoint unauthorized pulling if enabled via docker_public_enabledocker-public.s3-web.<name>.<dns_zone>

Terraform Module

Requirements

NameVersion
hcloud>=1.56.0
http>= 3.3.0

Providers

NameVersion
hcloud>=1.56.0
randomn/a

Resources

NameType
hcloud_primary_ip.ipv4resource
hcloud_primary_ip.ipv6resource
hcloud_server.serverresource
hcloud_volume.dataresource
hcloud_volume_attachment.dataresource
hcloud_zone_rrset.root_domain_catchall_ipv4resource
hcloud_zone_rrset.root_domain_ipv4resource
hcloud_zone_rrset.web_access_domains_ipv4resource
random_bytes.admin_tokenresource
random_bytes.docker_ro_default_passwordresource
random_bytes.docker_ro_default_userresource
random_bytes.docker_ro_passwordresource
random_bytes.docker_rw_default_passwordresource
random_bytes.docker_rw_default_userresource
random_bytes.docker_rw_passwordresource
random_bytes.metrics_tokenresource
random_bytes.owner_key_idsresource
random_bytes.owner_secret_keysresource
random_bytes.ro_key_idsresource
random_bytes.ro_secret_keysresource
random_bytes.rpc_secretresource
random_bytes.rw_key_idsresource
random_bytes.rw_secret_keysresource

Inputs

NameDescriptionTypeDefaultRequired
data_volume_sizedata volume size in GBnumber64no
datacentern/astring"nbg1-dc3"no
disable_volume_delete_protectionn/aboolfalseno
dns_zoneDNS zone to use for hostname and DNs entriesstringn/ayes
docker_enableEnable Docker registryboolfalseno
docker_public_enableEnable public anonymous access to Docker registry, see https://pellepelster.github.io/solidblocks/hetzner/web-s3-docker/#docker for detailsboolfalseno
docker_ro_usersDocker read-write users to provision. If no users is given a user will be auto.created, see https://pellepelster.github.io/solidblocks/hetzner/web-s3-docker/#docker for details
list(object({
username : string,
password : optional(string)
}))
[]no
docker_rw_usersDocker read-write users to provision. If no users is given a user will be auto.created, see https://pellepelster.github.io/solidblocks/hetzner/web-s3-docker/#docker for details
list(object({
username : string,
password : optional(string)
}))
[]no
labelsA list of labels to be attached to the server instance.map(any){}no
locationHetzner location to use for provisioned resourcesstring"nbg1"no
nameUnique name for the server instancestringn/ayes
s3_bucketsS3 buckets to provision, see https://pellepelster.github.io/solidblocks/hetzner/web-s3-docker/#s3-buckets for details
list(object({
name = string
owner_key_id = optional(string)
owner_secret_key = optional(string)
ro_key_id = optional(string)
ro_secret_key = optional(string)
rw_key_id = optional(string)
rw_secret_key = optional(string)
web_access_public_enable = optional(bool, false)
web_access_domains = optional(list(string))
}))
[]no
server_typeHetzner cloud server type, supports x86 and ARM architecturesstring"cx23"no
ssh_host_cert_ecdsaoverride generated ssh host ed25519 certificate, must be set alongside with ‘ssh_host_key_ecdsa’string""no
ssh_host_cert_ed25519override generated ssh host ed25519 certificate, must be set alongside with ‘ssh_host_key_ed25519’string""no
ssh_host_cert_rsaoverride generated ssh host ed25519 certificate, must be set alongside with ‘ssh_host_key_rsa’string""no
ssh_host_key_ecdsaoverride generated ssh host ed25519 key, must be set alongside with ‘ssh_host_cert_ecdsa’string""no
ssh_host_key_ed25519override generated ssh host ed25519 key, must be set alongside with ‘ssh_host_cert_ed25519’string""no
ssh_host_key_rsaoverride generated ssh host ed25519 key, must be set alongside with ‘ssh_host_cert_rsa’string""no
ssh_keysssh keys to provision for instance accesslist(number)n/ayes

Outputs

NameDescription
docker_host_privatefully qualified domain for the private docker registry
docker_host_publicfully qualified domain for the public docker registry if enabled
docker_ro_usersreadonly users for the docker registry
docker_rw_userswrite users for the docker registry
garage_admin_addressaddress for the GarageFS admin endpoint
garage_admin_tokentoken for the GarageFS admin endpoint
ipv4_addressIPv4 address of the created server
ipv6_addressIPv6 address of the created server
s3_bucketsthe created S3 bucket with access credentials and public endpoints if available
s3_hostfully qualified for the s3 endpoint
server_idHetzner ID of the created server