Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions nexus-apk-proxy/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
locals {
remote_url = "https://apk.cgr.dev/${var.chainguard_organization}"
}

# Split APKINDEX requests from package requests so each proxy can use its own
# cache TTL. The block rule goes on the packages proxy; the allow rule goes on
# the index proxy. Both reference the same regex.

resource "sonatyperepo_routing_rule" "block_index" {
name = "chainguard-apk-block-index"
description = "Block APKINDEX.tar.gz paths from the packages proxy."
mode = "BLOCK"
matchers = [".*APKINDEX\\.tar\\.gz"]
}

resource "sonatyperepo_routing_rule" "only_index" {
name = "chainguard-apk-only-index"
description = "Restrict the index proxy to APKINDEX.tar.gz paths only."
mode = "ALLOW"
matchers = [".*APKINDEX\\.tar\\.gz"]
}

# Packages proxy — handles .apk file requests.
# Packages are immutable, so content_max_age = -1 (never re-check the origin).
#
# MANUAL STEP REQUIRED: After applying, open this repository in the Nexus UI
# (Settings → Repository → Repositories → chainguard-apk-packages) and enable
# "Preserve encoded characters in URLs" under the HTTP section. This is not yet
# exposed by the Terraform provider. Without it, Nexus decodes percent-encoded
# characters (%2B, %2F, %3D) in the presigned R2 URLs that apk.cgr.dev
# redirects to, breaking the request signature.
resource "sonatyperepo_repository_raw_proxy" "packages" {
name = "chainguard-apk-packages"
online = true
routing_rule = sonatyperepo_routing_rule.block_index.name

storage = {
blob_store_name = var.blob_store_name
strict_content_type_validation = false
}

proxy = {
remote_url = local.remote_url
content_max_age = -1
metadata_max_age = -1
}

negative_cache = {
enabled = true
time_to_live = 1440
}

http_client = {
blocked = false
auto_block = true
authentication = {
type = "username"
username = var.chainguard_pull_token_username
password = var.chainguard_pull_token_password
}
}

raw = {
content_disposition = "ATTACHMENT"
}
}

# Index proxy — handles APKINDEX.tar.gz requests.
# The index is regenerated upstream whenever a package is added, so a short TTL
# is used. Tune index_max_age_minutes up to reduce origin fetches, down for
# faster visibility of newly published packages.
#
# MANUAL STEP REQUIRED: Same as above — enable "Preserve encoded characters in
# URLs" for chainguard-apk-index in the Nexus UI after applying.
resource "sonatyperepo_repository_raw_proxy" "index" {
name = "chainguard-apk-index"
online = true
routing_rule = sonatyperepo_routing_rule.only_index.name

storage = {
blob_store_name = var.blob_store_name
strict_content_type_validation = false
}

proxy = {
remote_url = local.remote_url
content_max_age = var.index_max_age_minutes
metadata_max_age = var.index_max_age_minutes
}

negative_cache = {
enabled = true
time_to_live = 1440
}

http_client = {
blocked = false
auto_block = true
authentication = {
type = "username"
username = var.chainguard_pull_token_username
password = var.chainguard_pull_token_password
}
}

raw = {
content_disposition = "ATTACHMENT"
}
}

# Group repository — the single URL clients point apk at.
# Packages proxy is listed first so its routing rule (BLOCK on APKINDEX) fires
# before the request falls through to the index proxy.
resource "sonatyperepo_repository_raw_group" "group" {
name = "chainguard-apk"
online = true

storage = {
blob_store_name = var.blob_store_name
strict_content_type_validation = false
}

group = {
member_names = [
sonatyperepo_repository_raw_proxy.packages.name,
sonatyperepo_repository_raw_proxy.index.name,
]
}

raw = {
content_disposition = "ATTACHMENT"
}
}
14 changes: 14 additions & 0 deletions nexus-apk-proxy/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
output "group_repository_url" {
description = "URL to use in /etc/apk/repositories. Point apk at this."
value = "${var.nexus_url}/repository/${sonatyperepo_repository_raw_group.group.name}"
}

output "packages_proxy_url" {
description = "Direct URL of the packages proxy (for reference / debugging)"
value = "${var.nexus_url}/repository/${sonatyperepo_repository_raw_proxy.packages.name}"
}

output "index_proxy_url" {
description = "Direct URL of the index proxy (for reference / debugging)"
value = "${var.nexus_url}/repository/${sonatyperepo_repository_raw_proxy.index.name}"
}
14 changes: 14 additions & 0 deletions nexus-apk-proxy/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
terraform {
required_providers {
sonatyperepo = {
source = "sonatype-nexus-community/sonatyperepo"
version = "~> 1.0"
}
}
}

provider "sonatyperepo" {
url = var.nexus_url
username = var.nexus_username
password = var.nexus_password
}
5 changes: 5 additions & 0 deletions nexus-apk-proxy/terraform.tfvars.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Enter your nexus URL and port #
nexus_url="http://nexus.local:80"

# Chainguard Organization name
chainguard_organization="justin.prince"
44 changes: 44 additions & 0 deletions nexus-apk-proxy/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
variable "nexus_url" {
description = "Base URL of the Nexus Repository Manager instance (e.g. http://localhost:8081)"
type = string
}

variable "nexus_username" {
description = "Nexus admin username"
type = string
default = "admin"
}

variable "nexus_password" {
description = "Nexus admin password"
type = string
sensitive = true
}

variable "chainguard_organization" {
description = "Chainguard organization name as shown in the Chainguard Console (used to form https://apk.cgr.dev/<organization>)"
type = string
}

variable "chainguard_pull_token_username" {
description = "Identity ID from 'chainctl auth pull-token --repository=apk' (the Username field)"
type = string
}

variable "chainguard_pull_token_password" {
description = "Token from 'chainctl auth pull-token --repository=apk' (the Password field)"
type = string
sensitive = true
}

variable "blob_store_name" {
description = "Name of the Nexus blob store to use for all created repositories"
type = string
default = "default"
}

variable "index_max_age_minutes" {
description = "Cache TTL for APKINDEX.tar.gz in minutes. Lower values give faster visibility of new packages at the cost of more origin fetches."
type = number
default = 15
}
Loading