Initial docker registry stack lifted from personal projects

This commit is contained in:
Greyscale 2024-12-06 17:44:29 +01:00
parent 6900486c53
commit 79931a1203
Signed by: grey
GPG key ID: DDB392AE64B32D89
9 changed files with 269 additions and 0 deletions

View file

@ -0,0 +1,43 @@
locals {
registry_users = {
for user in var.registry_users : user.username =>
user.password == null ? random_password.registry_users[user.username].result : user.password
}
# For each user, construct a htpasswd line
registry_user_pass_pairs = [
for user in local.registry_users : "${user}:${htpasswd_password.registry_users[user].bcrypt}"
]
registry_htpasswd = "${join("\n", local.registry_user_pass_pairs)}\n"
}
resource "random_password" "registry_users" {
for_each = toset(local.registry_users)
length = 32
special = false
}
resource "random_password" "salt" {
for_each = toset(local.registry_users)
length = 8
special = false
}
resource "htpasswd_password" "registry_users" {
for_each = random_password.registry_users
password = each.value.result
salt = random_password.salt[each.key].result
}
resource "docker_config" "docker_registry_htpasswd" {
name = "docker-registry-htpasswd-${replace(timestamp(), ":", ".")}"
data = base64encode(local.registry_htpasswd)
lifecycle {
ignore_changes = [name]
create_before_destroy = true
}
}
resource "local_file" "docker_registry_htpasswd" {
content = local.registry_htpasswd
filename = "${path.root}/.debug/docker-registry/htpasswd"
}
resource "random_password" "http_secret" {
for_each = toset(var.registry_users)
length = 16
}

View file

@ -0,0 +1,52 @@
variable "placement_constraints" {
default = []
type = list(string)
description = "Docker Swarm placement constraints"
}
variable "stack_name" {
type = string
default = "docker-registry"
description = "The name of the stack"
}
variable "registry_users" {
type = list(object({
username = string
password = optional(string)
}))
description = "A list of users to create in the registry"
}
variable "enable_delete" {
type = bool
default = false
description = "Enable the delete feature in the registry"
}
variable "s3_accesskey" {
type = string
description = "The access key for the S3 bucket"
}
variable "s3_secretkey" {
type = string
description = "The secret key for the S3 bucket"
}
variable "s3_region" {
type = string
description = "The region for the S3 bucket"
}
variable "s3_regionendpoint" {
type = string
description = "The region endpoint for the S3 bucket"
default = null
}
variable "s3_forcepathstyle" {
type = bool
description = "Force path style for the S3 bucket"
default = false
}
variable "s3_bucket" {
type = string
description = "The bucket name for the S3 bucket"
}
variable "domain" {
type = string
description = "The domain for the registry"
}

View file

@ -0,0 +1,15 @@
module "docker_registry_janitor" {
source = "../../docker/service"
stack_name = var.stack_name
service_name = "janitor"
image = "registry:2"
configs = {
"/etc/docker/registry/config.yml" = yamlencode(local.registry_config_yaml)
"/etc/docker/registry/htpasswd" = local.registry_htpasswd
}
restart_policy = "any"
restart_delay = "6h"
placement_constraints = var.placement_constraints
networks = [module.registry_network]
}

View file

@ -0,0 +1,4 @@
module "registry_network" {
source = "../../docker/network"
stack_name = "registry"
}

View file

@ -0,0 +1,7 @@
# Outputs
output "registry_users" {
value = {
for user in local.registry_users : user => nonsensitive(random_password.registry_users[user].result)
}
}

View file

@ -0,0 +1,6 @@
module "docker_registry_redis" {
source = "../../products/redis"
stack_name = var.stack_name
networks = [module.registry_network]
placement_constraints = var.placement_constraints
}

View file

@ -0,0 +1,84 @@
locals{
registry_config_yaml = {
version = 0.1
storage = {
s3 = {
accesskey = var.s3_accesskey
secretkey = var.s3_secretkey
region = var.s3_region
regionendpoint = var.s3_regionendpoint
forcepathstyle = var.s3_forcepathstyle
bucket = var.s3_bucket
}
delete = {
enabled = var.enable_delete
}
}
http = {
addr = "0.0.0.0:5000"
secret = random_password.http_secret.result
host = var.domain
headers = {
Access-Control-Allow-Origin = ["https://${var.domain}", ] // @todo add s3 domain here
Access-Control-Allow-Methods = ["HEAD", "GET", "DELETE", "OPTIONS"]
Access-Control-Allow-Credentials = ["true"]
Access-Control-Allow-Headers = ["Authorization", "Cache-Control", "Accept"]
Access-Control-Expose-Headers = ["Docker-Content-Digest"]
}
}
redis = {
addrs = ["${module.docker_registry_redis.service_name}:6379"]
password = module.docker_registry_redis.auth
db = 0
}
validation = {
manifests = {
urls = {
allow = [
"^https?://${var.domain}/"
]
}
}
}
auth = {
htpasswd = {
realm = "Registry Realm"
path = "/etc/docker/registry/htpasswd"
}
}
}
}
# Configuration file
module "docker_registry_config" {
source = "../../docker/config"
name = "docker-registry-config"
stack_name = var.stack_name
value = yamlencode(local.registry_config_yaml)
}
resource "local_file" "docker_registry_config_yml" {
content = yamlencode(local.registry_config_yaml)
filename = "${path.root}/.debug/docker-registry/config.yml"
}
# Registry Service
module "docker_registry" {
source = "../../docker/service"
stack_name = var.stack_name
service_name = "registry"
image = "registry:2"
configs = {
"/etc/docker/registry/config.yml" = nonsensitive(yamlencode(local.registry_config_yaml))
"/etc/docker/registry/htpasswd" = nonsensitive(local.registry_htpasswd)
}
restart_policy = "on-failure"
placement_constraints = var.placement_constraints
networks = [module.registry_network]
ports = [
{
host = 5000
container = 5000
}
]
}

View file

@ -0,0 +1,19 @@
terraform {
required_version = "~> 1.6"
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
htpasswd = {
source = "loafoe/htpasswd"
version = "~> 1.0"
}
}
}

View file

@ -0,0 +1,39 @@
# Registry UI
module "docker_registry_ui" {
source = "../../docker/service"
stack_name = var.stack_name
service_name = "ui"
image = "joxit/docker-registry-ui:main"
environment_variables = {
SINGLE_REGISTRY = "true"
REGISTRY_TITLE = "Grey.ooo Docker Registry"
NGINX_PROXY_PASS_URL = "http://${module.docker_registry.docker_service.name}:5000"
DELETE_IMAGES = "true"
SHOW_CONTENT_DIGEST = "true"
SHOW_CATALOG_NB_TAGS = "true"
CATALOG_MIN_BRANCHES = 1
CATALOG_MAX_BRANCHES = 1
TAGLIST_PAGE_SIZE = 100
REGISTRY_SECURED = false
CATALOG_ELEMENTS_LIMIT = 1000
}
networks = [module.registry_network, var.traefik.network, ]
placement_constraints = var.placement_constraints
traefik = var.traefik
}
variable "traefik" {
default = null
type = object({
domain = string
port = optional(number)
ssl = optional(bool, false)
rule = optional(string)
network = optional(object({
id = string
name = string
}))
})
description = "Whether to enable traefik for the service."
}