Terraform for a dedicated Google Cloud project that serves Linux packages from a public GCS bucket behind HTTPS, with GitHub Actions write access via Workload Identity Federation.
Only the repositories listed in github_repositories receive:
LIBOPS_PACKAGES_GCLOUD_OIDC_POOLLIBOPS_PACKAGES_GCLOUD_PROJECTLIBOPS_PACKAGES_GCLOUD_REGIONLIBOPS_PACKAGES_GSALIBOPS_PACKAGES_GCS_BUCKETLIBOPS_PACKAGES_PACKAGE_REPO_URLLIBOPS_PACKAGES_APTLY_GPG_KEY_IDLIBOPS_PACKAGES_APTLY_GPG_PRIVATE_KEY_SECRETLIBOPS_PACKAGES_APTLY_GPG_PASSPHRASE_SECRET
If github_actors is non-empty, the Workload Identity provider also restricts access to those actors. That check happens in the provider attribute_condition, so both repository and actor must match.
- Export credentials for both providers:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/admin-creds.json
export GITHUB_TOKEN=ghp_xxx- Review and copy the example variables:
cp terraform.tfvars.example terraform.tfvars- Initialize and apply:
terraform init
terraform plan
terraform apply- Add the Aptly GPG material out of band after Terraform creates the Secret Manager containers:
make create-aptly-gpg-key
make sync-aptly-gpg-key-idmake create-aptly-gpg-key prompts for the passphrase without echoing it and saves the revocation certificate under .out/gpg/.
- Delegate the reported DNS name servers for the package subdomain, for example
packages.libops.io, from the parent DNS zone.
This repo includes a local publishing path that mirrors the shared GitHub Actions workflow.
Prerequisites:
ghauthenticated for the source GitHub repositorydockergcloudauthenticated for Secret Manager and GCS on the host
By default the local targets use the published tooling image ghcr.io/libops/terraform-linux-packages:main.
To rebuild that image locally instead, run:
make package-tools-image-localExample:
make package GITHUB_REPOSITORY=libops/sitectl PACKAGE_NAME=sitectl RELEASE_VERSION=v1.2.3That target will:
- download the
.deband.rpmrelease assets withgh - pull the published Linux tooling image if needed
- fetch the GPG key material from Secret Manager inside the container
- rebuild the Debian and RPM repository metadata inside the container
- sync the result to the package bucket prefix for that package
On macOS, this avoids needing native installs of aptly or createrepo_c.
To print or sync the signing key ID from Secret Manager:
make print-aptly-gpg-key-id
make sync-aptly-gpg-key-id- The managed SSL certificate will stay in provisioning until the delegated package subdomain resolves to the created load balancer IP.
- The bucket is public so
aptand other package managers can fetch package metadata and artifacts without authentication. - This stack grants the GitHub service account bucket-level
roles/storage.objectAdmin, which is enough for syncing package repositories into the bucket. - Terraform creates the Secret Manager secret containers, but it does not write the private key or passphrase into Terraform state.
Below are the commands ran to get packages.libops.io setup using this repo
publish the latest version for our utils
terraform init
terraform apply
make create-aptly-gpg-key GCLOUD_PROJECT=libops-linux-packages
make sync-aptly-gpg-key-id
terraform apply
make package \
GITHUB_REPOSITORY=libops/sitectl \
PACKAGE_NAME=sitectl \
RELEASE_VERSION=v0.10.1
make package \
GITHUB_REPOSITORY=libops/sitectl-drupal \
PACKAGE_NAME=sitectl-drupal \
RELEASE_VERSION=v0.0.4
make package \
GITHUB_REPOSITORY=libops/sitectl-isle \
PACKAGE_NAME=sitectl-isle \
RELEASE_VERSION=v0.6.1| Name | Version |
|---|---|
| terraform | >= 1.2.4 |
| github | 6.11.1 |
| 7.24.0 |
| Name | Version |
|---|---|
| github.libops | 6.11.1 |
| 7.24.0 |
No modules.
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| aptly_gpg_key_id | GPG key ID Aptly uses to sign the published repository. | string |
"" |
no |
| aptly_gpg_passphrase_secret_id | Secret Manager secret ID that stores the Aptly GPG key passphrase. | string |
"aptly-gpg-passphrase" |
no |
| aptly_gpg_private_key_secret_id | Secret Manager secret ID that stores the armored Aptly private key. | string |
"aptly-gpg-private-key" |
no |
| billing_account | Google Cloud billing account ID. | string |
n/a | yes |
| bucket_location | Bucket location. | string |
"US" |
no |
| bucket_name | Name of the public package bucket. | string |
"libops-linux-packages" |
no |
| dns_zone_dns_name | DNS suffix managed by the zone, with trailing dot. | string |
"packages.libops.io." |
no |
| dns_zone_name | Cloud DNS managed zone name. | string |
"packages-libops-io" |
no |
| github_actors | Optional GitHub actors allowed to use the provider. Leave empty to allow any actor from the approved repositories. | set(string) |
[] |
no |
| github_owner | GitHub organization that owns the repositories allowed to publish packages. | string |
"libops" |
no |
| github_repositories | Full GitHub repository names allowed to impersonate the publishing service account. | set(string) |
[ |
no |
| org_id | Google Cloud organization ID. | string |
n/a | yes |
| package_domain | Fully qualified domain name that will serve the package repository. | string |
"packages.libops.io" |
no |
| project_id | Google Cloud project ID. | string |
"libops-linux-packages" |
no |
| project_name | Google Cloud project display name. | string |
"libops-linux-packages" |
no |
| region | Default Google Cloud region. | string |
"us-east5" |
no |
| Name | Description |
|---|---|
| aptly_gpg_passphrase_secret_id | Secret Manager secret ID for the Aptly key passphrase. |
| aptly_gpg_private_key_secret_id | Secret Manager secret ID for the armored Aptly private key. |
| bucket_name | Package bucket name. |
| dns_name_servers | Name servers assigned to the managed zone. Delegate the zone at your registrar or parent DNS. |
| github_service_account_email | Service account email used by GitHub Actions. |
| package_url | Public HTTPS package repository URL. |
| project_id | Google Cloud project ID. |
| workload_identity_provider | Full Workload Identity Provider resource name for GitHub Actions auth. |