10 files changed
+347
No files matched your search
@@ -0,0 +1,6 @@
|
||||
.git
|
||||
.gitea
|
||||
certs/.gitkeep
|
||||
scripts/refresh-certs.sh
|
||||
README.md
|
||||
.gitignore
|
||||
@@ -0,0 +1,4 @@
|
||||
*.sh text eol=lf
|
||||
*.yml text eol=lf
|
||||
docker-bake.hcl text eol=lf
|
||||
Dockerfile text eol=lf
|
||||
@@ -0,0 +1,69 @@
|
||||
name: Container
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ["v*"]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
concurrency:
|
||||
group: container-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Fetch AtlasHorizon CAs
|
||||
run: sh scripts/refresh-certs.sh
|
||||
|
||||
- name: Registry and image names
|
||||
run: |
|
||||
REGISTRY="${{ vars.REGISTRY }}"
|
||||
if [ -z "$REGISTRY" ]; then
|
||||
REGISTRY="${GITHUB_SERVER_URL#https://}"
|
||||
REGISTRY="${REGISTRY#http://}"
|
||||
fi
|
||||
IMAGE="${REGISTRY}/${GITHUB_REPOSITORY,,}"
|
||||
echo "REGISTRY=${REGISTRY}" >> "$GITHUB_ENV"
|
||||
echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV"
|
||||
echo "SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)" >> "$GITHUB_ENV"
|
||||
if [ "${{ github.ref }}" = "refs/heads/${{ github.event.repository.default_branch }}" ]; then
|
||||
echo "LATEST=true" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "LATEST=false" >> "$GITHUB_ENV"
|
||||
fi
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "MANUAL=true" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "MANUAL=false" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to the Gitea Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ secrets.REGISTRY_USERNAME || github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN || secrets.GITEA_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/bake-action@v6
|
||||
env:
|
||||
IMAGE: ${{ env.IMAGE }}
|
||||
LATEST: ${{ env.LATEST }}
|
||||
SHA: ${{ env.SHA }}
|
||||
MANUAL: ${{ env.MANUAL }}
|
||||
with:
|
||||
files: docker-bake.hcl
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
provenance: false
|
||||
@@ -0,0 +1,2 @@
|
||||
certs/*.crt
|
||||
certs/*.tmp
|
||||
@@ -0,0 +1,6 @@
|
||||
ARG BASE_IMAGE=debian:trixie-slim
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
COPY certs/*.crt /tmp/atlas-ca/
|
||||
COPY scripts/install-ca.sh /tmp/install-ca.sh
|
||||
RUN sh /tmp/install-ca.sh && rm -rf /tmp/install-ca.sh /tmp/atlas-ca
|
||||
@@ -0,0 +1,85 @@
|
||||
# AtlasHorizon PKI-trusted base images
|
||||
|
||||
Upstream OS bases with the AtlasHorizon PKI installed in the native system trust store. That is the only change.
|
||||
|
||||
Images are built from current CAs fetched at build/CI time from [http://pki.atlashorizon.net/certs/](http://pki.atlashorizon.net/certs/). Generated PEMs are not committed.
|
||||
|
||||
## Images
|
||||
|
||||
| Image | Upstream |
|
||||
| --- | --- |
|
||||
| `git.atlashorizon.net/atlashorizon/container-base/debian:trixie` | `debian:trixie` |
|
||||
| `git.atlashorizon.net/atlashorizon/container-base/debian-slim:trixie` | `debian:trixie-slim` |
|
||||
| `git.atlashorizon.net/atlashorizon/container-base/ubuntu:24.04` | `ubuntu:24.04` |
|
||||
| `git.atlashorizon.net/atlashorizon/container-base/alpine:3.24` | `alpine:3.24` |
|
||||
|
||||
On `main`, each family also gets a `:latest` tag (`debian:latest`, `debian-slim:latest`, `ubuntu:latest`, `alpine:latest`).
|
||||
|
||||
```dockerfile
|
||||
FROM git.atlashorizon.net/atlashorizon/container-base/debian-slim:trixie
|
||||
FROM git.atlashorizon.net/atlashorizon/container-base/ubuntu:24.04
|
||||
FROM git.atlashorizon.net/atlashorizon/container-base/alpine:3.24
|
||||
```
|
||||
|
||||
## Certificates
|
||||
|
||||
`scripts/refresh-certs.sh` downloads and converts (DER → PEM when needed):
|
||||
|
||||
- Root: `http://pki.atlashorizon.net/certs/AtlasHorizonRootCA.crt` (never `/root.crt`)
|
||||
- Enterprise: `http://pki.atlashorizon.net/certs/AtlasHorizon%20Enterprise%20CA.crt`
|
||||
- Issuing: `http://pki.atlashorizon.net/certs/AtlasHorizon%20Issuing%20CA.crt`
|
||||
|
||||
into `certs/AtlasHorizon-*-CA.crt`. Those files are gitignored. The Gitea Action runs the script after checkout so every image build embeds the current CAs.
|
||||
|
||||
Locally:
|
||||
|
||||
```bash
|
||||
sh scripts/refresh-certs.sh
|
||||
docker buildx bake
|
||||
```
|
||||
|
||||
Requires `curl` and `openssl`.
|
||||
|
||||
## Gitea Actions and registry
|
||||
|
||||
[`.gitea/workflows/container.yml`](.gitea/workflows/container.yml) fetches the CAs, builds all four images with Bake, and publishes to this Gitea instance’s OCI registry.
|
||||
|
||||
On `main`, version tags (`v*`), and manual **Run workflow**:
|
||||
|
||||
```
|
||||
git.atlashorizon.net/atlashorizon/container-base/debian:trixie
|
||||
git.atlashorizon.net/atlashorizon/container-base/debian:latest # default branch only
|
||||
git.atlashorizon.net/atlashorizon/container-base/debian:sha-<short-sha>
|
||||
git.atlashorizon.net/atlashorizon/container-base/debian:manual # workflow_dispatch
|
||||
```
|
||||
|
||||
(and the same tag set for `debian-slim`, `ubuntu`, `alpine`).
|
||||
|
||||
The host is taken from the Actions server URL (or `vars.REGISTRY` if you set one). The image prefix is `{registry}/{owner}/{repo}` in lowercase, which is what [Gitea’s container registry](https://docs.gitea.com/usage/packages/container) requires.
|
||||
|
||||
Pull requests **build** the images but do **not** push.
|
||||
|
||||
### One-time Gitea setup
|
||||
|
||||
1. Enable **Actions** on the repository (`Settings` → `Actions`).
|
||||
2. Enable **Packages** for the owner (user or org) if it is not already on.
|
||||
3. Register an **act_runner** that can build images: Docker socket or privileged `dind`. A label matching `runs-on: ubuntu-latest` is enough.
|
||||
4. If `actions/checkout@v4` and the Docker actions cannot be fetched, either set Gitea `[actions].DEFAULT_ACTIONS_URL = github` or mirror those actions onto this instance and change the `uses:` lines.
|
||||
|
||||
The workflow logs in as the user who triggered the job (`github.actor`) with `secrets.GITEA_TOKEN`. Give that token **package write** if the instance uses restricted token permissions.
|
||||
|
||||
If login returns **401**, create a PAT with **package read/write** and add repository secrets:
|
||||
|
||||
| Secret | Purpose |
|
||||
| --- | --- |
|
||||
| `REGISTRY_TOKEN` | PAT used instead of `GITEA_TOKEN` |
|
||||
| `REGISTRY_USERNAME` | Optional; defaults to the triggering user |
|
||||
|
||||
| Variable | Purpose |
|
||||
| --- | --- |
|
||||
| `REGISTRY` | Registry host if it is not the Gitea web hostname |
|
||||
|
||||
```bash
|
||||
docker login git.atlashorizon.net
|
||||
docker pull git.atlashorizon.net/atlashorizon/container-base/debian-slim:trixie
|
||||
```
|
||||
Whitespace-only changes.
@@ -0,0 +1,61 @@
|
||||
variable "IMAGE" {
|
||||
default = "git.atlashorizon.net/atlashorizon/container-base"
|
||||
}
|
||||
|
||||
variable "LATEST" {
|
||||
default = "false"
|
||||
}
|
||||
|
||||
variable "SHA" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "MANUAL" {
|
||||
default = "false"
|
||||
}
|
||||
|
||||
function "image_tags" {
|
||||
params = [family, version]
|
||||
result = compact([
|
||||
"${IMAGE}/${family}:${version}",
|
||||
equal(LATEST, "true") ? "${IMAGE}/${family}:latest" : "",
|
||||
notequal(SHA, "") ? "${IMAGE}/${family}:sha-${SHA}" : "",
|
||||
equal(MANUAL, "true") ? "${IMAGE}/${family}:manual" : "",
|
||||
])
|
||||
}
|
||||
|
||||
group "default" {
|
||||
targets = ["debian", "debian-slim", "ubuntu", "alpine"]
|
||||
}
|
||||
|
||||
target "debian" {
|
||||
dockerfile = "Dockerfile"
|
||||
args = {
|
||||
BASE_IMAGE = "debian:trixie"
|
||||
}
|
||||
tags = image_tags("debian", "trixie")
|
||||
}
|
||||
|
||||
target "debian-slim" {
|
||||
dockerfile = "Dockerfile"
|
||||
args = {
|
||||
BASE_IMAGE = "debian:trixie-slim"
|
||||
}
|
||||
tags = image_tags("debian-slim", "trixie")
|
||||
}
|
||||
|
||||
target "ubuntu" {
|
||||
dockerfile = "Dockerfile"
|
||||
args = {
|
||||
BASE_IMAGE = "ubuntu:24.04"
|
||||
}
|
||||
tags = image_tags("ubuntu", "24.04")
|
||||
}
|
||||
|
||||
target "alpine" {
|
||||
dockerfile = "Dockerfile"
|
||||
args = {
|
||||
BASE_IMAGE = "alpine:3.24"
|
||||
}
|
||||
tags = image_tags("alpine", "3.24")
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/bin/sh
|
||||
# Install AtlasHorizon CAs into the OS trust store (Debian/Ubuntu apt or Alpine apk).
|
||||
set -eu
|
||||
|
||||
SRC_DIR="/tmp/atlas-ca"
|
||||
CERT_DIR="/usr/local/share/ca-certificates"
|
||||
BUNDLE="/etc/ssl/certs/ca-certificates.crt"
|
||||
EXPECTED=3
|
||||
|
||||
count=0
|
||||
for cert in "${SRC_DIR}"/*.crt; do
|
||||
[ -f "${cert}" ] || continue
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
if [ "${count}" -ne "${EXPECTED}" ]; then
|
||||
echo "expected ${EXPECTED} CA files in ${SRC_DIR}, found ${count}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install the package first so its postinst does not consume our CAs yet.
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
apk add --no-cache ca-certificates
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends ca-certificates
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
else
|
||||
echo "unsupported base image: need apk or apt-get" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${CERT_DIR}"
|
||||
cp "${SRC_DIR}"/*.crt "${CERT_DIR}/"
|
||||
|
||||
output="$(update-ca-certificates 2>&1)"
|
||||
printf '%s\n' "${output}"
|
||||
|
||||
case "${output}" in
|
||||
*" added"*)
|
||||
added="$(printf '%s\n' "${output}" | sed -n 's/.*\([0-9][0-9]*\) added.*/\1/p' | head -n 1)"
|
||||
if [ -z "${added}" ] || [ "${added}" -lt "${EXPECTED}" ]; then
|
||||
echo "update-ca-certificates added ${added:-0} CAs, expected ${EXPECTED}" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ ! -f "${BUNDLE}" ]; then
|
||||
echo "missing trust bundle ${BUNDLE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for cert in "${CERT_DIR}"/*.crt; do
|
||||
body="$(sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' "${cert}" | sed '1d;$d' | head -n 1)"
|
||||
if [ -z "${body}" ]; then
|
||||
echo "could not read PEM body from ${cert}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q -F "${body}" "${BUNDLE}"; then
|
||||
echo "CA not present in ${BUNDLE}: ${cert}" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "AtlasHorizon PKI: ${EXPECTED} CAs installed in the system trust store"
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
# Download AtlasHorizon CAs from pki.atlashorizon.net/certs/ and write PEM files.
|
||||
# Never fetch /root.crt — that path is deprecated.
|
||||
set -eu
|
||||
|
||||
ROOT="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)"
|
||||
DEST="${ROOT}/certs"
|
||||
PKI="http://pki.atlashorizon.net/certs"
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo "curl is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v openssl >/dev/null 2>&1; then
|
||||
echo "openssl is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${DEST}"
|
||||
|
||||
# Convert DER or PEM input to a normalized PEM certificate.
|
||||
to_pem() {
|
||||
src="$1"
|
||||
dest="$2"
|
||||
if grep -q "BEGIN CERTIFICATE" "${src}"; then
|
||||
openssl x509 -in "${src}" -out "${dest}"
|
||||
else
|
||||
openssl x509 -inform DER -in "${src}" -out "${dest}"
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_pem() {
|
||||
url="$1"
|
||||
dest="$2"
|
||||
tmp="${dest}.tmp"
|
||||
echo "Fetching ${url}"
|
||||
curl -fsSL "${url}" -o "${tmp}"
|
||||
to_pem "${tmp}" "${dest}"
|
||||
rm -f "${tmp}"
|
||||
openssl x509 -in "${dest}" -noout -subject -issuer
|
||||
}
|
||||
|
||||
fetch_pem "${PKI}/AtlasHorizonRootCA.crt" "${DEST}/AtlasHorizon-Root-CA.crt"
|
||||
fetch_pem "${PKI}/AtlasHorizon%20Enterprise%20CA.crt" "${DEST}/AtlasHorizon-Enterprise-CA.crt"
|
||||
fetch_pem "${PKI}/AtlasHorizon%20Issuing%20CA.crt" "${DEST}/AtlasHorizon-Issuing-CA.crt"
|
||||
|
||||
echo "Wrote AtlasHorizon CAs to ${DEST}"
|
||||
Reference in new issue
Block a user