feat: migrate to Gitea, productize Helm chart, unify version
- CI: replace GitLab pipeline with Gitea Actions (.gitea/workflows/release.yaml); publish .deb/image/hosted/chart to Gitea registries; gate image+hosted on .deb (needs + registry pre-check) - drop connectone/Nexus; apt now from the Gitea Debian registry - single version source (/VERSION=3.2.8); CI enforces .env/Chart consistency - restructure: build/->packaging/deb, root Dockerfile->packaging/image, hosted/->packaging/hosted, .kube/->charts/freeradius - Helm chart: documented values, DB password via Secret/$ENV, configurable scheduling/persistence; remove plaintext creds and hostPath PV - gitignore *.env_cnf; docs for hosted (RU)/helm/root + agent KB sync Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
.git/
|
||||
.idea/
|
||||
.gitea/
|
||||
*.md
|
||||
.kube/
|
||||
build/
|
||||
.kube/
|
||||
charts/
|
||||
packaging/deb/
|
||||
agent/
|
||||
162
.gitea/workflows/release.yaml
Normal file
162
.gitea/workflows/release.yaml
Normal file
@@ -0,0 +1,162 @@
|
||||
name: release
|
||||
|
||||
# Manual release pipeline (Gitea Actions).
|
||||
#
|
||||
# Order / gating (".deb is the source of truth, hosted/image must not build without it"):
|
||||
# prepare ──► build-deb ──► build-image ──► package-helm
|
||||
# └──► package-hosted
|
||||
# build-image and package-hosted additionally verify the .deb exists in the
|
||||
# registry before doing anything (belt-and-suspenders gate).
|
||||
#
|
||||
# Required repo secrets:
|
||||
# PACKAGES_USER Gitea username (token owner)
|
||||
# PACKAGES_TOKEN Gitea access token with write:package (+ read:package)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
rlm_raw_branch:
|
||||
description: rlm_raw module branch
|
||||
required: false
|
||||
default: v3.2.4
|
||||
|
||||
concurrency:
|
||||
group: release
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
REGISTRY: git.ahax86.ru
|
||||
OWNER: pub
|
||||
IMAGE: git.ahax86.ru/pub/freeradius
|
||||
DEB_REGISTRY: https://git.ahax86.ru/api/packages/pub/debian
|
||||
DEB_DISTRIBUTION: noble
|
||||
DEB_COMPONENT: main
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.v.outputs.version }}
|
||||
radius_tag: ${{ steps.v.outputs.radius_tag }}
|
||||
pkg_version: ${{ steps.v.outputs.pkg_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- id: v
|
||||
name: Resolve version from /VERSION
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="$(tr -d ' \n\r' < VERSION)"
|
||||
radius_tag="release_$(echo "$version" | tr '.' '_')"
|
||||
pkg_version="${version}+git"
|
||||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||
echo "radius_tag=$radius_tag" >> "$GITHUB_OUTPUT"
|
||||
echo "pkg_version=$pkg_version" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$version tag=$radius_tag pkg=$pkg_version"
|
||||
- name: Check version consistency
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="$(tr -d ' \n\r' < VERSION)"
|
||||
env_tag="$(grep -E '^FREERADIUS_TAG=' packaging/hosted/.env | cut -d= -f2 | tr -d ' \r')"
|
||||
chart_app="$(grep -E '^appVersion:' charts/freeradius/Chart.yaml | sed -E 's/appVersion:[[:space:]]*"?([^"]+)"?/\1/' | tr -d ' \r')"
|
||||
[ "$env_tag" = "$version" ] || { echo "hosted/.env FREERADIUS_TAG=$env_tag != VERSION=$version"; exit 1; }
|
||||
[ "$chart_app" = "$version" ] || { echo "Chart appVersion=$chart_app != VERSION=$version"; exit 1; }
|
||||
echo "Versions consistent: $version"
|
||||
|
||||
build-deb:
|
||||
needs: [prepare]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build .deb builder image
|
||||
run: |
|
||||
docker build packaging/deb -f packaging/deb/Dockerfile \
|
||||
--build-arg RADIUS_TAG=${{ needs.prepare.outputs.radius_tag }} \
|
||||
--build-arg RLM_RAW_BRANCH=${{ inputs.rlm_raw_branch }} \
|
||||
-t freerad-builder:${{ needs.prepare.outputs.version }}
|
||||
- name: Upload .deb to Gitea Debian registry
|
||||
env:
|
||||
DEB_USER: ${{ secrets.PACKAGES_USER }}
|
||||
DEB_PASS: ${{ secrets.PACKAGES_TOKEN }}
|
||||
run: |
|
||||
docker run --rm \
|
||||
-e DEB_USER -e DEB_PASS \
|
||||
-e DEB_REGISTRY="${{ env.DEB_REGISTRY }}" \
|
||||
-e DEB_DISTRIBUTION="${{ env.DEB_DISTRIBUTION }}" \
|
||||
-e DEB_COMPONENT="${{ env.DEB_COMPONENT }}" \
|
||||
freerad-builder:${{ needs.prepare.outputs.version }} ./upload.sh
|
||||
|
||||
build-image:
|
||||
needs: [prepare, build-deb]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Verify .deb is published
|
||||
env:
|
||||
DEB_USER: ${{ secrets.PACKAGES_USER }}
|
||||
DEB_PASS: ${{ secrets.PACKAGES_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
url="${{ env.DEB_REGISTRY }}/dists/${{ env.DEB_DISTRIBUTION }}/${{ env.DEB_COMPONENT }}/binary-amd64/Packages"
|
||||
curl -fsSL --user "$DEB_USER:$DEB_PASS" "$url" \
|
||||
| grep -q "Version: ${{ needs.prepare.outputs.pkg_version }}" \
|
||||
|| { echo "Package ${{ needs.prepare.outputs.pkg_version }} not found in registry"; exit 1; }
|
||||
- name: Login to container registry
|
||||
run: echo "${{ secrets.PACKAGES_TOKEN }}" | docker login ${{ env.REGISTRY }} -u "${{ secrets.PACKAGES_USER }}" --password-stdin
|
||||
- name: Build and push image
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker build -f packaging/image/Dockerfile \
|
||||
--build-arg PKG_VERSION=${{ needs.prepare.outputs.pkg_version }} \
|
||||
-t ${{ env.IMAGE }}:${{ needs.prepare.outputs.version }} \
|
||||
-t ${{ env.IMAGE }}:latest .
|
||||
docker push ${{ env.IMAGE }}:${{ needs.prepare.outputs.version }}
|
||||
docker push ${{ env.IMAGE }}:latest
|
||||
|
||||
package-hosted:
|
||||
needs: [prepare, build-deb]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Verify .deb is published
|
||||
env:
|
||||
DEB_USER: ${{ secrets.PACKAGES_USER }}
|
||||
DEB_PASS: ${{ secrets.PACKAGES_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
url="${{ env.DEB_REGISTRY }}/dists/${{ env.DEB_DISTRIBUTION }}/${{ env.DEB_COMPONENT }}/binary-amd64/Packages"
|
||||
curl -fsSL --user "$DEB_USER:$DEB_PASS" "$url" \
|
||||
| grep -q "Version: ${{ needs.prepare.outputs.pkg_version }}" \
|
||||
|| { echo "Package ${{ needs.prepare.outputs.pkg_version }} not found in registry"; exit 1; }
|
||||
- name: Package and upload hosted bundle
|
||||
env:
|
||||
PKG_USER: ${{ secrets.PACKAGES_USER }}
|
||||
PKG_TOKEN: ${{ secrets.PACKAGES_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chmod +x packaging/hosted/install.sh packaging/hosted/update.sh
|
||||
tar -czf hosted.tar.gz -C packaging hosted
|
||||
ver="${{ needs.prepare.outputs.version }}"
|
||||
curl -fsSL --user "$PKG_USER:$PKG_TOKEN" \
|
||||
--upload-file hosted.tar.gz \
|
||||
"https://${{ env.REGISTRY }}/api/packages/${{ env.OWNER }}/generic/hosted/${ver}/hosted.tar.gz"
|
||||
|
||||
package-helm:
|
||||
needs: [prepare, build-image]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install helm
|
||||
run: curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
- name: Lint chart
|
||||
run: helm lint charts/freeradius --set db.password=ci
|
||||
- name: Package and push chart
|
||||
env:
|
||||
PKG_USER: ${{ secrets.PACKAGES_USER }}
|
||||
PKG_TOKEN: ${{ secrets.PACKAGES_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
helm package charts/freeradius --app-version "${{ needs.prepare.outputs.version }}" -d dist
|
||||
chart="$(ls dist/*.tgz)"
|
||||
curl -fsSL --user "$PKG_USER:$PKG_TOKEN" -X POST \
|
||||
--upload-file "$chart" \
|
||||
"https://${{ env.REGISTRY }}/api/packages/${{ env.OWNER }}/helm/api/charts"
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
.idea/
|
||||
pkg/
|
||||
bin/
|
||||
|
||||
# Local secrets for the hosted install — never commit (only *.env_cnf_example is tracked)
|
||||
*.env_cnf
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
variables:
|
||||
CI_REGISTRY: registry.connectone.pro
|
||||
CONTAINER_IMAGE: ${CI_REGISTRY}/${CI_PROJECT_PATH}:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}
|
||||
RADIUS_TAG: "release_3_2_7"
|
||||
|
||||
stages:
|
||||
- release
|
||||
- release-deb
|
||||
- release-hosted
|
||||
|
||||
release:
|
||||
stage: release
|
||||
image: docker:latest
|
||||
before_script:
|
||||
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
|
||||
script:
|
||||
- docker build -t ${CONTAINER_IMAGE} .
|
||||
- docker push ${CONTAINER_IMAGE}
|
||||
when: manual
|
||||
only:
|
||||
- main
|
||||
|
||||
release-deb:
|
||||
stage: release-deb
|
||||
image: docker:latest
|
||||
variables:
|
||||
DOCKER_BUILDKIT: 1
|
||||
script:
|
||||
- cd build
|
||||
- docker build --build-arg RADIUS_TAG=${RADIUS_TAG} . -t freerad_builder:3.2.x -f Dockerfile
|
||||
- docker run -e DEB_USER=${DEB_USER} -e DEB_PASS="${DEB_PASS}" freerad_builder:3.2.x ./upload.sh
|
||||
when: manual
|
||||
only:
|
||||
- main
|
||||
|
||||
release-hosted:
|
||||
stage: release-hosted
|
||||
image: alpine:latest
|
||||
variables:
|
||||
VERSION: "${RADIUS_TAG}"
|
||||
script:
|
||||
- apk add --no-cache curl
|
||||
- chmod a+x hosted/install.sh hosted/update.sh
|
||||
- tar -czvf hosted.tar.gz hosted
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file hosted.tar.gz "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/hosted/${VERSION}/hosted.tar.gz"'
|
||||
when: manual
|
||||
only:
|
||||
- main
|
||||
@@ -1,4 +0,0 @@
|
||||
apiVersion: v2
|
||||
name: freeradius
|
||||
version: 1.0.1
|
||||
appVersion: 3.2.0
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{.Release.Name}}-registry-key
|
||||
namespace: {{.Release.Namespace}}
|
||||
data:
|
||||
.dockerconfigjson: "{{.Values.registryAuthBase64}}"
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
@@ -1,70 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{.Release.Name}}
|
||||
namespace: {{.Release.Namespace}}
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{.Release.Name}}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{.Release.Name}}
|
||||
spec:
|
||||
nodeSelector:
|
||||
freeradius: enabled
|
||||
tolerations:
|
||||
- key: freeradius
|
||||
operator: Equal
|
||||
value: enabled
|
||||
effect: NoSchedule
|
||||
imagePullSecrets:
|
||||
- name: {{.Release.Name}}-registry-key
|
||||
{{/* securityContext:*/}}
|
||||
{{/* runAsUser: 108*/}}
|
||||
{{/* runAsGroup: 112*/}}
|
||||
{{/* fsGroup: 108*/}}
|
||||
containers:
|
||||
- name: {{.Release.Name}}
|
||||
image: {{.Values.image.name}}
|
||||
imagePullPolicy: {{.Values.image.pullPolicy}}
|
||||
env:
|
||||
- name: MY_POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
|
||||
- name: TZ
|
||||
value: UTC
|
||||
- name: HOSTNAME
|
||||
value: freeradius
|
||||
args: ["freeradius", "-X"]
|
||||
ports:
|
||||
- containerPort: {{.Values.ports.main}}
|
||||
- containerPort: {{.Values.ports.second}}
|
||||
volumeMounts:
|
||||
- mountPath: /etc/freeradius/certs
|
||||
name: freeradius-cert-data
|
||||
readOnly: false
|
||||
- name: configs-volume
|
||||
mountPath: "/configs/default"
|
||||
subPath: default
|
||||
readOnly: true
|
||||
- name: configs-volume
|
||||
mountPath: "/configs/sql"
|
||||
subPath: sql
|
||||
readOnly: true
|
||||
- name: configs-volume
|
||||
mountPath: "/configs/rest"
|
||||
subPath: rest
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: configs-volume
|
||||
configMap:
|
||||
name: {{ .Release.Name }}-configs
|
||||
defaultMode: 488
|
||||
- name: freeradius-cert-data
|
||||
persistentVolumeClaim:
|
||||
claimName: freeradius-data-claim
|
||||
@@ -1,19 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{.Release.Name}}-external
|
||||
namespace: {{.Release.Namespace}}
|
||||
spec:
|
||||
type: NodePort
|
||||
externalTrafficPolicy: Local
|
||||
ports:
|
||||
- port: {{.Values.ports.main}}
|
||||
nodePort: {{.Values.ports.main}}
|
||||
protocol: UDP
|
||||
name: freerad-first
|
||||
- port: {{.Values.ports.second}}
|
||||
nodePort: {{.Values.ports.second}}
|
||||
protocol: UDP
|
||||
name: freerad-second
|
||||
selector:
|
||||
app: {{ .Chart.Name }}
|
||||
@@ -1,26 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: freeradius-data-disk
|
||||
namespace: {{.Release.Namespace}}
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Mi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
hostPath:
|
||||
path: {{.Values.storageBase}}/{{.Chart.Name}}
|
||||
---
|
||||
kind: PersistentVolumeClaim
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: freeradius-data-claim
|
||||
namespace: {{.Release.Namespace}}
|
||||
spec:
|
||||
volumeName: freeradius-data-disk
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Mi
|
||||
@@ -1,20 +0,0 @@
|
||||
image:
|
||||
name: ${CONTAINER_IMAGE}
|
||||
pullPolicy: "IfNotPresent"
|
||||
registryAuthBase64: ${REGISTRY_AUTH}
|
||||
storageBase: ${STORAGE_BASE}
|
||||
|
||||
ports:
|
||||
main: 31812
|
||||
second: 31813
|
||||
|
||||
db:
|
||||
host: "postgresql-pgbouncer"
|
||||
port: 6432
|
||||
user: "admin"
|
||||
password: "radius_db"
|
||||
name: "radius_db"
|
||||
|
||||
api:
|
||||
host: "hotspotter"
|
||||
port: 8080
|
||||
60
AGENTS.md
Normal file
60
AGENTS.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Что это
|
||||
|
||||
Репозиторий упаковки и развёртывания кастомной сборки FreeRADIUS 3.2.x для
|
||||
hotspot/captive-portal сценариев. Прикладного кода нет: это сборочные Dockerfile,
|
||||
shell-скрипты, Helm/k8s-манифесты и
|
||||
конфиги FreeRADIUS. Профиль каркаса: infra-lite.
|
||||
|
||||
Главный контекст: [agent/INDEX.md](agent/INDEX.md). Читать перед любыми изменениями.
|
||||
|
||||
Сборка и поставка - через Gitea (`git.ahax86.ru`, owner `pub`): Debian-реестр для .deb,
|
||||
container registry для образа, Helm-реестр для чарта, generic-пакет для hosted-бандла.
|
||||
Nexus и GitLab больше не используются.
|
||||
|
||||
## Порядок чтения в начале сессии
|
||||
|
||||
1. `AGENTS.md` (этот файл).
|
||||
2. `agent/INDEX.md` - назначение, артефакты, режимы развёртывания, версии, риски.
|
||||
|
||||
## Структура репозитория
|
||||
|
||||
```
|
||||
VERSION ← единый источник версии FreeRADIUS (3.2.8)
|
||||
.gitea/workflows/release.yaml ← ручной релиз (Gitea Actions): prepare/build-deb/build-image/package-hosted/package-helm
|
||||
packaging/
|
||||
deb/ ← сборка .deb из исходников FreeRADIUS + rlm_raw, upload в Debian-реестр Gitea
|
||||
image/ ← рантайм-образ (Dockerfile + entrypoint.sh + local-noble.list); ставит .deb, печёт конфиг
|
||||
hosted/ ← bare-metal/VM установка (install.sh, update.sh, systemd, cron, config)
|
||||
config/ ← radiusd.conf, dictionary, clients.conf, mods-enabled/*, sites-enabled/*
|
||||
.env / .env_cnf* ← версия пакета и подстановки (хосты, порты, креды БД)
|
||||
charts/
|
||||
freeradius/ ← продуктовый Helm-чарт (Deployment, ConfigMap, Secret, PVC, Service)
|
||||
```
|
||||
|
||||
## Инструменты и команды
|
||||
|
||||
- Сборка/выкладка - только через Gitea Actions (`.gitea/workflows/release.yaml`, `workflow_dispatch`).
|
||||
- Версия задаётся в `VERSION`; CI выводит из неё тег/пин/appVersion и падает при рассинхроне.
|
||||
- Локальная сборка .deb: `docker build packaging/deb -f packaging/deb/Dockerfile --build-arg RADIUS_TAG=release_3_2_8 --build-arg RLM_RAW_BRANCH=v3.2.4 -t freerad-builder:3.2.8`
|
||||
- Локальная сборка образа: `docker build -f packaging/image/Dockerfile --build-arg PKG_VERSION=3.2.8+git -t freeradius:3.2.8 .` (контекст = корень).
|
||||
- Тест hosted: `cd packaging/hosted && docker build . -t hosted:test -f Dockerfile`
|
||||
- Установка на ноду (bare-metal): `packaging/hosted/install.sh` (root), обновление версии: `packaging/hosted/update.sh`.
|
||||
- Чарт: `helm lint charts/freeradius --set db.password=test`, `helm template charts/freeradius --set db.password=test`.
|
||||
- Линтеров/тестов кода в репозитории нет.
|
||||
|
||||
## Правила репо
|
||||
|
||||
1. Ничего не публиковать (push, PR, CI-стейдж, деплой) без явной команды пользователя.
|
||||
2. `git add` строго поимённо. Никогда `git add .` или `git add -A`.
|
||||
3. `.env` / `.env_cnf` не читать целиком и не коммитить - там креды БД. Проверять
|
||||
переменную через grep по `^VAR=`. `.env_cnf` хранит секреты и не должен попадать в git.
|
||||
4. Деструктивные действия с прод-нодами, БД и k8s - только с явного подтверждения.
|
||||
5. Изменился конфиг, версия, хост или инвариант - синхронно отразить в `agent/INDEX.md`.
|
||||
6. Долгоживущие решения фиксируются в `agent/INDEX.md`. Журнал сессий не ведётся по умолчанию.
|
||||
7. Документация краткая и строгая: без галлюцинаций, факты берём из кода и конфигов.
|
||||
|
||||
## Язык
|
||||
|
||||
Отвечать по-русски, кратко, без длинных тире.
|
||||
30
Dockerfile
30
Dockerfile
@@ -1,30 +0,0 @@
|
||||
FROM ubuntu:24.04
|
||||
WORKDIR /tmp
|
||||
ARG out_dir=/etc/freeradius
|
||||
COPY local-noble.list /etc/apt/sources.list.d/ubuntu.sources
|
||||
RUN apt-get update && apt-get install -y apt-transport-https ca-certificates curl software-properties-common gpg
|
||||
RUN curl -fsSL http://connectone.pro/gpg | gpg --dearmor -o /usr/share/keyrings/connectone.gpg && \
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/connectone.gpg] \
|
||||
https://nexus.connectone.pro/repository/radius bionic main" | tee /etc/apt/sources.list.d/connectone.list \
|
||||
> /dev/null
|
||||
RUN apt-get update && apt-get install -y libfreeradius3=3.2.8+git freeradius-common=3.2.8+git \
|
||||
freeradius-postgresql=3.2.8+git freeradius-rest=3.2.8+git freeradius-utils=3.2.8+git freeradius=3.2.8+git && \
|
||||
apt autoremove --purge -y && apt-get clean
|
||||
COPY hosted /tmp/hosted
|
||||
RUN cp hosted/config/radiusd.conf $out_dir/radiusd.conf && \
|
||||
cp hosted/config/dictionary $out_dir/dictionary && \
|
||||
cp hosted/config/clients.conf $out_dir/clients.conf && \
|
||||
cp hosted/config/mods-enabled/raw $out_dir/mods-enabled/raw && \
|
||||
cp hosted/config/sites-enabled/dynamic-clients $out_dir/sites-enabled/dynamic-clients && \
|
||||
rm -f /etc/freeradius/sites-enabled/inner-tunnel && \
|
||||
rm -f /etc/freeradius/mods-enabled/eap \
|
||||
RUN rm -f /etc/freeradius/mods-enabled/delay && \
|
||||
ln -s $out_dir/mods-available/delay $out_dir/mods-enabled/delay
|
||||
ADD entrypoint.sh /usr/bin/entrypoint.sh
|
||||
RUN chmod a+x /usr/bin/entrypoint.sh && chmod -R 750 $out_dir && chown -R freerad:root $out_dir && \
|
||||
mv /etc/freeradius/certs /tmp && \
|
||||
rm -rf /tmp/hosted
|
||||
|
||||
WORKDIR /etc/freeradius
|
||||
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
|
||||
CMD ["freeradius", "-f"]
|
||||
93
README.md
93
README.md
@@ -1,20 +1,87 @@
|
||||
# freeradius
|
||||
# FreeRADIUS
|
||||
|
||||
build:
|
||||
```bash
|
||||
cd build
|
||||
docker build . -t freerad_builder:3.2.x -f Dockerfile_build
|
||||
docker run -e DEB_USER=${DEB_USER} DEB_PASS="${DEB_PASS}" freerad_builder:3.2.x ./upload.sh
|
||||
Packaging and delivery of a custom FreeRADIUS 3.2.x build for
|
||||
hotspot / captive-portal scenarios. There is no application code here — the repo
|
||||
builds upstream FreeRADIUS with the extra `rlm_raw` module and ships it to two
|
||||
runtimes (bare-metal and Kubernetes) via a Gitea-hosted registry.
|
||||
|
||||
## Single source of version truth
|
||||
|
||||
[`VERSION`](VERSION) holds the FreeRADIUS release (`3.2.8`). CI derives everything
|
||||
from it: the build tag `release_3_2_8`, the package pin `3.2.8+git`, the chart
|
||||
`appVersion`. CI fails if `packaging/hosted/.env` or `charts/freeradius/Chart.yaml`
|
||||
drift from `VERSION`.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
VERSION single source of the FreeRADIUS version
|
||||
.gitea/workflows/release.yaml manual release pipeline (Gitea Actions)
|
||||
packaging/
|
||||
deb/ build the .deb from FreeRADIUS source + rlm_raw, upload to registry
|
||||
image/ runtime container image (installs the .deb, bakes configs)
|
||||
hosted/ bare-metal / VM install (install.sh, update.sh, config, cron)
|
||||
charts/
|
||||
freeradius/ productized Helm chart
|
||||
```
|
||||
|
||||
label nodes:
|
||||
```bash
|
||||
kubectl label node connect-prod-0 freeradius=enabled
|
||||
## Artifacts (Gitea, owner `pub`)
|
||||
|
||||
| Artifact | Registry |
|
||||
|----------|----------|
|
||||
| `.deb` packages | `https://git.ahax86.ru/api/packages/pub/debian` (`noble main`) |
|
||||
| Container image | `git.ahax86.ru/pub/freeradius:<version>` |
|
||||
| Hosted bundle | `.../api/packages/pub/generic/hosted/<version>/hosted.tar.gz` |
|
||||
| Helm chart | `https://git.ahax86.ru/api/packages/pub/helm` |
|
||||
|
||||
## Release flow (`.gitea/workflows/release.yaml`, manual)
|
||||
|
||||
```
|
||||
prepare ──► build-deb ──► build-image ──► package-helm
|
||||
└──► package-hosted
|
||||
```
|
||||
|
||||
remove
|
||||
```bash
|
||||
The `.deb` is the prerequisite: `build-image` and `package-hosted` both `needs:
|
||||
build-deb` **and** verify the pinned version exists in the Debian registry
|
||||
before running. Trigger from Gitea → Actions → `release` → *Run workflow*.
|
||||
|
||||
sudo apt-mark unhold libfreeradius3 freeradius-common freeradius-postgresql freeradius-rest freeradius-utils freeradius
|
||||
sudo dpkg --force-overwrite --purge freeradius-common freeradius-config freeradius-python3 freeradius libfreeradius3 freeradius-postgresql freeradius-rest freeradius-utils && sudo rm -rf /etc/freeradius && sudo rm -rf /usr/lib/freeradius
|
||||
### CI setup (Gitea)
|
||||
|
||||
1. **Runner.** Enable Actions for the repo and register an `ubuntu-latest` runner
|
||||
(`act_runner`) with Docker available. If your runner uses a different label,
|
||||
change `runs-on` in the workflow.
|
||||
2. **Access token.** Gitea → avatar → *Settings* → *Applications* →
|
||||
*Manage Access Tokens* → *Generate New Token*. Scopes: `write:package` and
|
||||
`read:package`. Copy the token (shown once).
|
||||
3. **Repo secrets.** Repo → *Settings* → *Actions* → *Secrets* → *Add Secret*:
|
||||
- `PACKAGES_USER` — your Gitea username (the token owner).
|
||||
- `PACKAGES_TOKEN` — the token from step 2.
|
||||
|
||||
These secrets are used to push to the container / Debian / generic / Helm
|
||||
registries and to read the Debian `Packages` index in the gate check.
|
||||
|
||||
## Local builds (no upload)
|
||||
|
||||
```bash
|
||||
# .deb builder image
|
||||
docker build packaging/deb -f packaging/deb/Dockerfile \
|
||||
--build-arg RADIUS_TAG=release_3_2_8 --build-arg RLM_RAW_BRANCH=v3.2.4 \
|
||||
-t freerad-builder:3.2.8
|
||||
|
||||
# runtime image (context = repo root; pulls .deb from the Debian registry)
|
||||
docker build -f packaging/image/Dockerfile --build-arg PKG_VERSION=3.2.8+git -t freeradius:3.2.8 .
|
||||
|
||||
# chart
|
||||
helm lint charts/freeradius --set db.password=test
|
||||
helm template freeradius charts/freeradius --set db.password=test
|
||||
```
|
||||
|
||||
## Deployment docs
|
||||
|
||||
- Bare-metal / VM: [packaging/hosted/README.md](packaging/hosted/README.md)
|
||||
- Kubernetes (Helm): [charts/freeradius/README.md](charts/freeradius/README.md)
|
||||
|
||||
## Internals
|
||||
|
||||
See [agent/INDEX.md](agent/INDEX.md) for invariants, hosts/ports, backends and
|
||||
known constraints.
|
||||
|
||||
104
agent/INDEX.md
Normal file
104
agent/INDEX.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# FreeRADIUS - обзор инфраструктуры
|
||||
|
||||
Профиль: infra-lite. Бизнес-логику самого FreeRADIUS здесь не описываем.
|
||||
|
||||
## Назначение
|
||||
|
||||
Сборка и поставка кастомного FreeRADIUS 3.2.x для hotspot/captive-portal сценариев.
|
||||
Репозиторий не содержит сервиса - он упаковывает апстримный FreeRADIUS с
|
||||
доп. модулем `rlm_raw` и накладывает конфиги под две среды выполнения.
|
||||
|
||||
## Версия - единый источник
|
||||
|
||||
`VERSION` (корень) = `3.2.8`. Из него CI выводит:
|
||||
- `RADIUS_TAG=release_3_2_8` (тег исходников FreeRADIUS для сборки .deb),
|
||||
- пин пакетов `3.2.8+git` (образ и hosted),
|
||||
- `appVersion` чарта.
|
||||
|
||||
CI (`prepare`) падает, если `packaging/hosted/.env` (`FREERADIUS_TAG`) или
|
||||
`charts/freeradius/Chart.yaml` (`appVersion`) разошлись с `VERSION`. Ветка
|
||||
`rlm_raw` - отдельный параметр (`workflow_dispatch` input, дефолт `v3.2.4`).
|
||||
|
||||
## Артефакты (Gitea, owner `pub`)
|
||||
|
||||
| Артефакт | Реестр |
|
||||
|----------|--------|
|
||||
| `.deb` | `https://git.ahax86.ru/api/packages/pub/debian` (distribution `noble`, component `main`) |
|
||||
| Образ | `git.ahax86.ru/pub/freeradius:<version>` (+ `latest`) |
|
||||
| Hosted-бандл | `.../api/packages/pub/generic/hosted/<version>/hosted.tar.gz` |
|
||||
| Helm-чарт | `https://git.ahax86.ru/api/packages/pub/helm` |
|
||||
|
||||
Соглашение: локальный alias helm-репо - `ahax86` (`helm repo add ahax86 ...`).
|
||||
|
||||
## Релизный поток (`.gitea/workflows/release.yaml`, `workflow_dispatch`)
|
||||
|
||||
```
|
||||
prepare ──► build-deb ──► build-image ──► package-helm
|
||||
└──► package-hosted
|
||||
```
|
||||
|
||||
- `.deb` - источник истины: `build-image` и `package-hosted` имеют `needs: build-deb`
|
||||
И дополнительно проверяют, что версия `3.2.8+git` уже опубликована в Debian-реестре
|
||||
(GET `dists/noble/main/binary-amd64/Packages`), иначе fail.
|
||||
- Авторизация: репо-секреты `PACKAGES_USER` / `PACKAGES_TOKEN` (Gitea token, scope
|
||||
`write:package`).
|
||||
- Раннер: `ubuntu-latest` с доступным Docker.
|
||||
|
||||
## Режимы развёртывания
|
||||
|
||||
- Kubernetes (`charts/freeradius/`): продуктовый Helm-чарт. Образ из `build-image`.
|
||||
Конфиги `default`/`rest` - в ConfigMap; `sql` - тоже в ConfigMap, но пароль БД не
|
||||
хранится там, а подставляется в рантайме через `$ENV{DB_PASSWORD}` из Secret
|
||||
(`db.password` или `db.existingSecret`). `entrypoint.sh` копирует `/configs/*` в
|
||||
`/etc/freeradius`. Сертификаты на PVC (`persistence`). nodeSelector/tolerations -
|
||||
конфигурируемые (по умолчанию пусто). Установка - из Helm-реестра Gitea.
|
||||
- Hosted / bare-metal (`packaging/hosted/install.sh`, root): ставит запиненные .deb из
|
||||
Debian-реестра Gitea (`apt-mark hold`), подставляет хосты/порты/креды в конфиги через
|
||||
`sed` из `.env_cnf`, ставит systemd-юнит и cron на ежемесячный перевыпуск сертификатов
|
||||
(`cron/radius-renew.sh`). `update.sh` бампит версию пакета (`unhold` -> install -> hold).
|
||||
Для приватного реестра - `DEB_AUTH="user:token"`.
|
||||
|
||||
## Внешние зависимости и хосты
|
||||
|
||||
- Gitea: `git.ahax86.ru`, owner `pub`. Образы - container registry, .deb - Debian-реестр,
|
||||
чарт - Helm-реестр, hosted - generic-пакет. GPG-ключ apt-репо:
|
||||
`https://git.ahax86.ru/api/packages/pub/debian/repository.key`.
|
||||
- PostgreSQL `radius_db` (схема RADIUS: `radacct`, `radcheck`, `nas`, ...). В k8s -
|
||||
`postgresql-pgbouncer:6432`, юзер `admin`; в hosted - из `.env_cnf`.
|
||||
- REST API `hotspotter` (k8s: `hotspotter:8080`, эндпоинт `radius/auth`). Используется
|
||||
как fallback-авторизация NAS, когда клиента нет в таблице `nas`.
|
||||
|
||||
## Сетевые порты
|
||||
|
||||
- Auth/acct: 31812 / 31813 (см. site `default`, `values.yaml` `service.*`, `.env_cnf`).
|
||||
- В k8s публикуются Service'ом (по умолчанию NodePort 31812/31813).
|
||||
|
||||
## Инварианты
|
||||
|
||||
- Клиенты (NAS) резолвятся динамически: site `dynamic-clients` ищет NAS по `shortname`
|
||||
(= `NAS-Identifier`) в таблице `nas`; если нет - идёт `rest` к `hotspotter`. Статический
|
||||
`clients.conf` минимален.
|
||||
- Включён модуль `delay` (delay_reject); удалены `eap`, `inner-tunnel`, `proxy_rate_limit`
|
||||
(синхронно в образе и hosted).
|
||||
- Модуль `rlm_raw` обязателен: конфиги используют `%{raw:...}` (Called-Station-Id,
|
||||
NAS-Identifier). Без него сборка/конфиг не работают.
|
||||
- Сертификаты в k8s переживают рестарты через PVC; `entrypoint.sh` копирует дефолтные
|
||||
только если каталог `certs` пуст.
|
||||
|
||||
## Риски и known warts
|
||||
|
||||
- Образ при сборке тянет .deb из Debian-реестра Gitea - предполагается публичный read для
|
||||
owner `pub`. Если реестр приватный, сборку образа надо чинить (read-токен/buildkit secret),
|
||||
а на нодах нужен `DEB_AUTH` для apt.
|
||||
- Реестр может отклонить повторную заливку той же версии .deb (релиз - переиздание версии
|
||||
требует bump `VERSION`).
|
||||
- Пароль БД: в hosted - в `.env_cnf` (gitignored, `*.env_cnf`); в k8s - в Secret. В git не
|
||||
попадает.
|
||||
|
||||
## Рабочие правила
|
||||
|
||||
- Менять конфиги/версии осознанно: одно изменение часто нужно отразить и в чарте
|
||||
(`charts/freeradius/`), и в hosted (`packaging/hosted/`), и в образе (`packaging/image/`).
|
||||
- Версию менять только в `VERSION` (плюс синхронно `.env`/`Chart.yaml`, иначе CI упадёт).
|
||||
- Прод-выкладка только вручную через Gitea Actions и только по команде пользователя.
|
||||
- Долгоживущие решения дописывать сюда. Держать обзор кратким и актуальным.
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
DEB_REPO=${DEB_REPO:-"https://nexus.connectone.pro/repository/radius/"}
|
||||
|
||||
for file in *.deb
|
||||
do
|
||||
curl -u "${DEB_USER}:${DEB_PASS}" -H "Content-Type: multipart/form-data" --data-binary "@./$file" "${DEB_REPO}"
|
||||
done; # file
|
||||
9
charts/freeradius/.helmignore
Normal file
9
charts/freeradius/.helmignore
Normal file
@@ -0,0 +1,9 @@
|
||||
# Patterns to ignore when building Helm packages.
|
||||
.DS_Store
|
||||
.git/
|
||||
.gitignore
|
||||
*.tmp
|
||||
*.bak
|
||||
*.orig
|
||||
*.swp
|
||||
README.md.tmpl
|
||||
11
charts/freeradius/Chart.yaml
Normal file
11
charts/freeradius/Chart.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: v2
|
||||
name: freeradius
|
||||
description: Custom FreeRADIUS 3.2.x with the rlm_raw module, PostgreSQL backend and REST-based dynamic clients.
|
||||
type: application
|
||||
# Chart version (semver). Bump on chart changes.
|
||||
version: 1.2.0
|
||||
# Application version. Kept in sync with the repo-wide /VERSION by CI.
|
||||
appVersion: "3.2.8"
|
||||
home: https://git.ahax86.ru/pub/freeradius
|
||||
sources:
|
||||
- https://git.ahax86.ru/pub/freeradius
|
||||
83
charts/freeradius/README.md
Normal file
83
charts/freeradius/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# FreeRADIUS Helm chart
|
||||
|
||||
Deploys the custom FreeRADIUS 3.2.x build — with the `rlm_raw`
|
||||
module, PostgreSQL backend and REST-based dynamic clients — to Kubernetes.
|
||||
|
||||
The chart is published to the Gitea Helm registry by CI:
|
||||
`https://git.ahax86.ru/api/packages/pub/helm`
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The container image must be published (CI job `build-image`):
|
||||
`git.ahax86.ru/pub/freeradius:<version>`.
|
||||
- A reachable PostgreSQL with the RADIUS schema (`radacct`, `radcheck`, `nas`, ...).
|
||||
- The REST `hotspotter` service (fallback NAS authorization).
|
||||
- If the image registry is private: a pre-created `docker-registry` Secret,
|
||||
referenced via `imagePullSecrets`.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
helm repo add --username <user> --password <token> \
|
||||
ahax86 https://git.ahax86.ru/api/packages/pub/helm
|
||||
helm repo update
|
||||
|
||||
helm upgrade --install freeradius ahax86/freeradius \
|
||||
--namespace radius --create-namespace \
|
||||
--set db.host=postgresql-pgbouncer \
|
||||
--set db.password=<db-password>
|
||||
```
|
||||
|
||||
Use an existing Secret for the DB password instead of `--set db.password`:
|
||||
|
||||
```bash
|
||||
helm upgrade --install freeradius ahax86/freeradius -n radius \
|
||||
--set db.existingSecret=freeradius-db --set db.passwordKey=password
|
||||
```
|
||||
|
||||
`db.password` (or `db.existingSecret`) is required — install fails otherwise.
|
||||
|
||||
## How config is delivered
|
||||
|
||||
- `default` (virtual server) and `rest` (hotspotter) render into a ConfigMap.
|
||||
- `sql` renders into the same ConfigMap but the password is **not** stored there:
|
||||
it is read at runtime via `$ENV{DB_PASSWORD}`, injected from the DB Secret.
|
||||
- The entrypoint copies `/configs/{default,sql,rest}` into `/etc/freeradius`.
|
||||
- Certificates live on a PVC and survive restarts.
|
||||
|
||||
## Values
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `replicaCount` | `1` | Pod replicas. |
|
||||
| `image.repository` | `git.ahax86.ru/pub/freeradius` | Image repo. |
|
||||
| `image.tag` | `""` | Empty → chart `appVersion`. |
|
||||
| `image.pullPolicy` | `IfNotPresent` | |
|
||||
| `imagePullSecrets` | `[]` | Names of pull secrets. |
|
||||
| `args` | `[freeradius, -f]` | Use `[freeradius, -X]` for debug. |
|
||||
| `service.type` | `NodePort` | `ClusterIP` / `NodePort` / `LoadBalancer`. |
|
||||
| `service.authPort` / `service.acctPort` | `31812` / `31813` | UDP ports. |
|
||||
| `service.authNodePort` / `service.acctNodePort` | `31812` / `31813` | NodePort only. |
|
||||
| `service.externalTrafficPolicy` | `Local` | NodePort only. |
|
||||
| `db.host` / `db.port` | `postgresql-pgbouncer` / `6432` | PostgreSQL. |
|
||||
| `db.user` / `db.name` | `admin` / `radius_db` | |
|
||||
| `db.password` | `""` | Required unless `db.existingSecret`. |
|
||||
| `db.existingSecret` / `db.passwordKey` | `""` / `db-password` | Use an existing Secret. |
|
||||
| `api.host` / `api.port` | `hotspotter` / `8080` | REST hotspotter. |
|
||||
| `persistence.enabled` | `true` | Cert PVC (else `emptyDir`). |
|
||||
| `persistence.existingClaim` | `""` | Use a pre-created PVC. |
|
||||
| `persistence.storageClass` | `""` | |
|
||||
| `persistence.size` | `32Mi` | |
|
||||
| `resources` / `nodeSelector` / `tolerations` / `affinity` | `{}` / `{}` / `[]` / `{}` | Scheduling. |
|
||||
| `env` | `{TZ: UTC}` | Extra container env. |
|
||||
|
||||
> Note: the previous deployment pinned `nodeSelector freeradius=enabled` and a
|
||||
> matching toleration. These are no longer hardcoded — set them via
|
||||
> `nodeSelector` / `tolerations` if your nodes are tainted.
|
||||
|
||||
## Render locally
|
||||
|
||||
```bash
|
||||
helm template freeradius charts/freeradius --set db.password=test | less
|
||||
helm lint charts/freeradius --set db.password=test
|
||||
```
|
||||
20
charts/freeradius/templates/NOTES.txt
Normal file
20
charts/freeradius/templates/NOTES.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
FreeRADIUS ({{ .Chart.AppVersion }}) released as {{ .Release.Name }} in namespace {{ .Release.Namespace }}.
|
||||
|
||||
Image: {{ include "freeradius.image" . }}
|
||||
Service: {{ include "freeradius.fullname" . }} ({{ .Values.service.type }})
|
||||
auth: UDP {{ .Values.service.authPort }}{{ if eq .Values.service.type "NodePort" }} (nodePort {{ .Values.service.authNodePort }}){{ end }}
|
||||
acct: UDP {{ .Values.service.acctPort }}{{ if eq .Values.service.type "NodePort" }} (nodePort {{ .Values.service.acctNodePort }}){{ end }}
|
||||
|
||||
Backends:
|
||||
PostgreSQL: {{ .Values.db.host }}:{{ .Values.db.port }} db={{ .Values.db.name }} user={{ .Values.db.user }}
|
||||
REST (hotspotter): http://{{ .Values.api.host }}:{{ .Values.api.port }}/
|
||||
|
||||
{{- if and (not .Values.db.existingSecret) (not .Values.db.password) }}
|
||||
|
||||
WARNING: db.password is empty and no db.existingSecret was provided.
|
||||
Set one of them, e.g.: helm upgrade {{ .Release.Name }} ... --set db.password=<password>
|
||||
{{- end }}
|
||||
|
||||
Check status:
|
||||
kubectl -n {{ .Release.Namespace }} rollout status deploy/{{ include "freeradius.fullname" . }}
|
||||
kubectl -n {{ .Release.Namespace }} logs deploy/{{ include "freeradius.fullname" . }}
|
||||
63
charts/freeradius/templates/_helpers.tpl
Normal file
63
charts/freeradius/templates/_helpers.tpl
Normal file
@@ -0,0 +1,63 @@
|
||||
{{/* Chart name */}}
|
||||
{{- define "freeradius.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Fully qualified app name */}}
|
||||
{{- define "freeradius.fullname" -}}
|
||||
{{- if .Values.fullnameOverride -}}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- if contains $name .Release.Name -}}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Common labels */}}
|
||||
{{- define "freeradius.labels" -}}
|
||||
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{ include "freeradius.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Selector labels */}}
|
||||
{{- define "freeradius.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "freeradius.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Container image ref (tag defaults to appVersion) */}}
|
||||
{{- define "freeradius.image" -}}
|
||||
{{- $tag := .Values.image.tag | default .Chart.AppVersion -}}
|
||||
{{- printf "%s:%s" .Values.image.repository $tag -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Name of the Secret holding the DB password */}}
|
||||
{{- define "freeradius.dbSecretName" -}}
|
||||
{{- if .Values.db.existingSecret -}}
|
||||
{{- .Values.db.existingSecret -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-db" (include "freeradius.fullname" .) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Key inside the DB Secret */}}
|
||||
{{- define "freeradius.dbSecretKey" -}}
|
||||
{{- .Values.db.passwordKey | default "db-password" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Name of the PVC used for certs */}}
|
||||
{{- define "freeradius.pvcName" -}}
|
||||
{{- if .Values.persistence.existingClaim -}}
|
||||
{{- .Values.persistence.existingClaim -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-certs" (include "freeradius.fullname" .) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -1,17 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-configs
|
||||
name: {{ include "freeradius.fullname" . }}-configs
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app: clickhouse-operator
|
||||
{{- include "freeradius.labels" . | nindent 4 }}
|
||||
data:
|
||||
default: |
|
||||
server default {
|
||||
listen {
|
||||
type = auth
|
||||
ipaddr = *
|
||||
port = {{.Values.ports.main}}
|
||||
port = {{ .Values.service.authPort }}
|
||||
limit {
|
||||
max_connections = 16
|
||||
lifetime = 0
|
||||
@@ -21,7 +21,7 @@ data:
|
||||
|
||||
listen {
|
||||
ipaddr = *
|
||||
port = {{.Values.ports.second}}
|
||||
port = {{ .Values.service.acctPort }}
|
||||
type = acct
|
||||
|
||||
limit {
|
||||
@@ -99,7 +99,6 @@ data:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sql: |
|
||||
sql {
|
||||
dialect = "postgresql"
|
||||
@@ -111,7 +110,7 @@ data:
|
||||
server = "{{ .Values.db.host }}"
|
||||
port = {{ .Values.db.port }}
|
||||
login = "{{ .Values.db.user }}"
|
||||
password = "{{.Values.db.password}}"
|
||||
password = "$ENV{DB_PASSWORD}"
|
||||
radius_db = "{{ .Values.db.name }}"
|
||||
|
||||
acct_table1 = "radacct"
|
||||
@@ -167,4 +166,3 @@ data:
|
||||
idle_timeout = 60
|
||||
}
|
||||
}
|
||||
|
||||
93
charts/freeradius/templates/deployment.yaml
Normal file
93
charts/freeradius/templates/deployment.yaml
Normal file
@@ -0,0 +1,93 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "freeradius.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "freeradius.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "freeradius.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "freeradius.selectorLabels" . | nindent 8 }}
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ include "freeradius.name" . }}
|
||||
image: {{ include "freeradius.image" . | quote }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
args: {{ toJson .Values.args }}
|
||||
env:
|
||||
- name: MY_POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "freeradius.dbSecretName" . }}
|
||||
key: {{ include "freeradius.dbSecretKey" . }}
|
||||
{{- range $k, $v := .Values.env }}
|
||||
- name: {{ $k }}
|
||||
value: {{ $v | quote }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: auth
|
||||
containerPort: {{ .Values.service.authPort }}
|
||||
protocol: UDP
|
||||
- name: acct
|
||||
containerPort: {{ .Values.service.acctPort }}
|
||||
protocol: UDP
|
||||
{{- with .Values.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
volumeMounts:
|
||||
- name: certs
|
||||
mountPath: /etc/freeradius/certs
|
||||
- name: configs
|
||||
mountPath: /configs/default
|
||||
subPath: default
|
||||
readOnly: true
|
||||
- name: configs
|
||||
mountPath: /configs/sql
|
||||
subPath: sql
|
||||
readOnly: true
|
||||
- name: configs
|
||||
mountPath: /configs/rest
|
||||
subPath: rest
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: configs
|
||||
configMap:
|
||||
name: {{ include "freeradius.fullname" . }}-configs
|
||||
- name: certs
|
||||
{{- if .Values.persistence.enabled }}
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "freeradius.pvcName" . }}
|
||||
{{- else }}
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
18
charts/freeradius/templates/pvc.yaml
Normal file
18
charts/freeradius/templates/pvc.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "freeradius.pvcName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "freeradius.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode }}
|
||||
{{- if .Values.persistence.storageClass }}
|
||||
storageClassName: {{ .Values.persistence.storageClass | quote }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size | quote }}
|
||||
{{- end }}
|
||||
12
charts/freeradius/templates/secret.yaml
Normal file
12
charts/freeradius/templates/secret.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
{{- if not .Values.db.existingSecret }}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ printf "%s-db" (include "freeradius.fullname" .) }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "freeradius.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
{{ include "freeradius.dbSecretKey" . }}: {{ required "db.password is required (or set db.existingSecret)" .Values.db.password | quote }}
|
||||
{{- end }}
|
||||
29
charts/freeradius/templates/service.yaml
Normal file
29
charts/freeradius/templates/service.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "freeradius.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "freeradius.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
{{- if eq .Values.service.type "NodePort" }}
|
||||
externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: auth
|
||||
port: {{ .Values.service.authPort }}
|
||||
targetPort: auth
|
||||
protocol: UDP
|
||||
{{- if eq .Values.service.type "NodePort" }}
|
||||
nodePort: {{ .Values.service.authNodePort }}
|
||||
{{- end }}
|
||||
- name: acct
|
||||
port: {{ .Values.service.acctPort }}
|
||||
targetPort: acct
|
||||
protocol: UDP
|
||||
{{- if eq .Values.service.type "NodePort" }}
|
||||
nodePort: {{ .Values.service.acctNodePort }}
|
||||
{{- end }}
|
||||
selector:
|
||||
{{- include "freeradius.selectorLabels" . | nindent 4 }}
|
||||
74
charts/freeradius/values.yaml
Normal file
74
charts/freeradius/values.yaml
Normal file
@@ -0,0 +1,74 @@
|
||||
# Default values for the freeradius chart.
|
||||
# This is a YAML-formatted file. All keys are documented below.
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
# Container image built by this repo and pushed to the Gitea registry.
|
||||
repository: git.ahax86.ru/pub/freeradius
|
||||
# Image tag. Empty -> defaults to the chart appVersion (e.g. 3.2.8).
|
||||
tag: ""
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
# Names of pre-created docker-registry secrets used to pull the image.
|
||||
imagePullSecrets: []
|
||||
# - name: gitea-registry
|
||||
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
# Run arguments for the container. Use ["freeradius","-X"] for debug logging.
|
||||
args:
|
||||
- freeradius
|
||||
- -f
|
||||
|
||||
# Auth / accounting service. FreeRADIUS uses UDP.
|
||||
service:
|
||||
# ClusterIP | NodePort | LoadBalancer
|
||||
type: NodePort
|
||||
authPort: 31812
|
||||
acctPort: 31813
|
||||
# nodePort values are used only when type: NodePort.
|
||||
authNodePort: 31812
|
||||
acctNodePort: 31813
|
||||
externalTrafficPolicy: Local
|
||||
|
||||
# PostgreSQL backend (RADIUS schema: radacct, radcheck, nas, ...).
|
||||
# The password is injected into the pod as the DB_PASSWORD env var and read by
|
||||
# FreeRADIUS via $ENV{DB_PASSWORD}; it is never written into a ConfigMap.
|
||||
db:
|
||||
host: postgresql-pgbouncer
|
||||
port: 6432
|
||||
user: admin
|
||||
name: radius_db
|
||||
# Provide the password at install time (never commit a real value), e.g.
|
||||
# helm install ... --set db.password=...
|
||||
password: ""
|
||||
# Or reference an existing Secret instead of db.password:
|
||||
existingSecret: ""
|
||||
passwordKey: db-password
|
||||
|
||||
# REST fallback authorization (hotspotter): used when a NAS is not in the `nas` table.
|
||||
api:
|
||||
host: hotspotter
|
||||
port: 8080
|
||||
|
||||
# Certificate persistence. Certs are generated on first start and must survive
|
||||
# restarts. Mounted at /etc/freeradius/certs.
|
||||
persistence:
|
||||
enabled: true
|
||||
# Use a pre-created PVC instead of letting the chart create one.
|
||||
existingClaim: ""
|
||||
storageClass: ""
|
||||
accessMode: ReadWriteOnce
|
||||
size: 32Mi
|
||||
|
||||
# Extra environment variables for the container.
|
||||
env:
|
||||
TZ: UTC
|
||||
|
||||
resources: {}
|
||||
podAnnotations: {}
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
@@ -1,14 +0,0 @@
|
||||
# hosted
|
||||
|
||||
to test
|
||||
|
||||
build
|
||||
```bash
|
||||
docker build . -t hosted:test -f Dockerfile
|
||||
```
|
||||
|
||||
run
|
||||
|
||||
```bash
|
||||
docker run -ti -p 1812:1812/UDP -p 1813:1813/UDP hosted:test /bin/bash
|
||||
```
|
||||
@@ -10,8 +10,10 @@ COPY local-noble.list /etc/apt/sources.list.d/ubuntu.sources
|
||||
RUN apt-get update && apt-get install -y devscripts equivs git quilt gcc
|
||||
|
||||
#
|
||||
# Setup build envs
|
||||
ARG RADIUS_TAG="release_3_2_5"
|
||||
# Setup build envs.
|
||||
# Defaults match the repo-wide /VERSION (3.2.8). CI overrides RADIUS_TAG
|
||||
# from VERSION; RLM_RAW_BRANCH is the rlm_raw module branch for this release.
|
||||
ARG RADIUS_TAG="release_3_2_8"
|
||||
ARG RLM_RAW_BRANCH="v3.2.4"
|
||||
|
||||
ENV RADIUS_REPO="https://github.com/FreeRADIUS/freeradius-server.git"
|
||||
41
packaging/deb/upload.sh
Normal file
41
packaging/deb/upload.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Uploads built .deb packages to a Gitea Debian package registry.
|
||||
#
|
||||
# Required env:
|
||||
# DEB_USER - Gitea username (token owner)
|
||||
# DEB_PASS - Gitea access token with the write:package scope
|
||||
#
|
||||
# Optional env (defaults target git.ahax86.ru / owner "pub"):
|
||||
# DEB_REGISTRY - registry base URL
|
||||
# DEB_DISTRIBUTION - apt distribution (default: noble)
|
||||
# DEB_COMPONENT - apt component (default: main)
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
DEB_REGISTRY="${DEB_REGISTRY:-https://git.ahax86.ru/api/packages/pub/debian}"
|
||||
DEB_DISTRIBUTION="${DEB_DISTRIBUTION:-noble}"
|
||||
DEB_COMPONENT="${DEB_COMPONENT:-main}"
|
||||
|
||||
if [ -z "${DEB_USER:-}" ] || [ -z "${DEB_PASS:-}" ]; then
|
||||
echo "DEB_USER and DEB_PASS must be set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
upload_url="${DEB_REGISTRY}/pool/${DEB_DISTRIBUTION}/${DEB_COMPONENT}/upload"
|
||||
|
||||
shopt -s nullglob
|
||||
debs=(*.deb)
|
||||
if [ "${#debs[@]}" -eq 0 ]; then
|
||||
echo "No .deb files found to upload" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for file in "${debs[@]}"; do
|
||||
echo "Uploading ${file} -> ${upload_url}"
|
||||
curl -fsSL --user "${DEB_USER}:${DEB_PASS}" \
|
||||
--upload-file "./${file}" \
|
||||
"${upload_url}"
|
||||
done
|
||||
|
||||
echo "Uploaded ${#debs[@]} package(s)"
|
||||
120
packaging/hosted/README.md
Normal file
120
packaging/hosted/README.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Hosted установка (bare-metal / VM)
|
||||
|
||||
## Для чего этот репозиторий
|
||||
|
||||
Это сборка и поставка кастомного **FreeRADIUS 3.2.x** для hotspot / captive-portal
|
||||
сценариев. Прикладного кода здесь нет: репозиторий собирает апстримный FreeRADIUS с
|
||||
дополнительным модулем `rlm_raw`, накладывает конфиги и доставляет результат в двух
|
||||
видах - bare-metal (этот каталог) и Kubernetes (Helm-чарт в `charts/freeradius`).
|
||||
|
||||
## Как это работает
|
||||
|
||||
1. Точки доступа / hotspot-шлюзы (NAS) шлют RADIUS-запросы. Статически клиенты **не**
|
||||
прописаны - они резолвятся динамически. `clients.conf` объявляет сеть `0.0.0.0/0`
|
||||
как динамическую (`client dynamic`).
|
||||
2. Когда приходит пакет от неизвестного NAS, FreeRADIUS вызывает виртуальный сервер
|
||||
`dynamic_clients` (`sites-enabled/dynamic-clients`). Тот ищет NAS по `NAS-Identifier`
|
||||
в таблице `nas` PostgreSQL: если запись есть - берёт из БД `secret`/`shortname` и
|
||||
определяет клиента на лету; если нет - идёт в REST к сервису `hotspotter`.
|
||||
3. Авторизация абонента идёт в site `default` через SQL (схема RADIUS: `radcheck`,
|
||||
`radreply`, `radacct`, ...) и/или REST. Hotspotter получает `hotspot-group`
|
||||
(= `Called-Station-Id`) и `hotspot-id` (= `NAS-Identifier`) и решает, пускать ли.
|
||||
4. Профиль урезан под задачу: отключены `eap`, `inner-tunnel`, `proxy_rate_limit`,
|
||||
включён `delay_reject`.
|
||||
|
||||
## Зачем нужен rlm_raw
|
||||
|
||||
`NAS-Identifier` и `Called-Station-Id` - строковые RADIUS-атрибуты, в которые
|
||||
hotspot-шлюзы кладут идентификатор точки/группы. Это значение должно совпадать
|
||||
**байт-в-байт** с `nas.shortname` в БД и с записями в hotspotter.
|
||||
|
||||
Штатный xlat `%{NAS-Identifier}` отдаёт значение уже после разбора и нормализации
|
||||
FreeRADIUS (атрибут декодируется по словарю, к строке применяется обработка). Для
|
||||
произвольных байтов и нестандартных форматов это меняет значение и ломает поиск.
|
||||
|
||||
Модуль `rlm_raw` добавляет xlat `%{raw:...}`, который возвращает атрибут ровно так,
|
||||
как его прислал NAS, без перекодирования. Все ключевые конфиги (`dynamic-clients`,
|
||||
`mods-enabled/rest`) построены на `%{raw:Called-Station-Id}` и `%{raw:NAS-Identifier}`,
|
||||
поэтому модуль обязателен - без него конфиг просто не загрузится. Именно поэтому
|
||||
FreeRADIUS пересобирается из исходников с `rlm_raw`, а не ставится из стандартного apt.
|
||||
|
||||
## Что в этом каталоге
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|------------|
|
||||
| `.env` | Версия пакета: `FREERADIUS_TAG` (ставится как `<tag>+git`). |
|
||||
| `.env_cnf_example` | Шаблон для `.env_cnf`. Скопировать и заполнить. |
|
||||
| `.env_cnf` | Локальные секреты/хосты. **В git не попадает** (gitignore). |
|
||||
| `config/` | radiusd.conf, dictionary, clients.conf, mods/sites. |
|
||||
| `install.sh` | Первичная установка (root). |
|
||||
| `update.sh` | Смена версии пакета (root). |
|
||||
| `cron/radius-renew.sh` | Ежемесячный перевыпуск сертификатов. |
|
||||
|
||||
Этот бандл публикуется CI как generic-пакет:
|
||||
`https://git.ahax86.ru/api/packages/pub/generic/hosted/<version>/hosted.tar.gz`
|
||||
|
||||
## Требования
|
||||
|
||||
- Ubuntu 24.04 (noble), root.
|
||||
- Доступ к `git.ahax86.ru`, к PostgreSQL и к REST-сервису `hotspotter`.
|
||||
- Нужная версия `.deb` уже опубликована в Debian-реестре Gitea (CI собирает её первой).
|
||||
|
||||
## Установка
|
||||
|
||||
```bash
|
||||
cp .env_cnf_example .env_cnf
|
||||
$EDITOR .env_cnf # заполнить хосты/порты/креды
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
`install.sh` подключает Debian-реестр Gitea, ставит запиненные пакеты (`apt-mark hold`),
|
||||
рендерит `sites-enabled/default`, `mods-enabled/rest` и `mods-enabled/sql` из `.env_cnf`,
|
||||
копирует статические конфиги, включает `delay_reject`, ставит systemd-юнит и cron.
|
||||
|
||||
Переменные `.env_cnf`:
|
||||
|
||||
| Переменная | Смысл |
|
||||
|------------|-------|
|
||||
| `FR_MAIN_PORT` / `FR_ACC_PORT` | UDP-порты auth / accounting. |
|
||||
| `FR_API_HOST` / `FR_API_PORT` | Endpoint REST hotspotter. |
|
||||
| `FR_PG_HOST` / `FR_PG_PORT` | Хост / порт PostgreSQL. |
|
||||
| `FR_PG_USER` / `FR_PG_PASS` / `FR_PG_DB` | Креды / база PostgreSQL. |
|
||||
|
||||
### Приватный реестр
|
||||
|
||||
Если реестр `pub` не публичный, перед запуском задайте токен:
|
||||
|
||||
```bash
|
||||
export DEB_AUTH="<gitea-user>:<token-с-read:package>"
|
||||
sudo -E ./install.sh
|
||||
```
|
||||
|
||||
Прочие переопределения (значения по умолчанию):
|
||||
`DEB_REGISTRY=https://git.ahax86.ru/api/packages/pub/debian`,
|
||||
`DEB_DISTRIBUTION=noble`, `DEB_COMPONENT=main`.
|
||||
|
||||
## Смена версии
|
||||
|
||||
```bash
|
||||
# поменять FREERADIUS_TAG в .env, затем:
|
||||
sudo ./update.sh
|
||||
```
|
||||
|
||||
`update.sh`: `apt-mark unhold` → установка нужной версии → `apt-mark hold` → перезапуск.
|
||||
|
||||
## Удаление
|
||||
|
||||
```bash
|
||||
sudo apt-mark unhold libfreeradius3 freeradius-common freeradius-postgresql \
|
||||
freeradius-rest freeradius-utils freeradius
|
||||
sudo apt-get purge -y freeradius freeradius-common freeradius-config \
|
||||
freeradius-python3 libfreeradius3 freeradius-postgresql freeradius-rest freeradius-utils
|
||||
sudo rm -rf /etc/freeradius /usr/lib/freeradius
|
||||
```
|
||||
|
||||
## Проверка в Docker
|
||||
|
||||
```bash
|
||||
docker build . -t hosted:test -f Dockerfile
|
||||
docker run -ti -p 1812:1812/udp -p 1813:1813/udp hosted:test /bin/bash
|
||||
```
|
||||
@@ -46,7 +46,6 @@ rest {
|
||||
#
|
||||
# If you wish to disable this pre-caching and reachability check,
|
||||
# comment out the configuration item below.
|
||||
#connect_uri = "http://connectone.me:31668"
|
||||
connect_uri = "http://#API_HOST#:#API_PORT#/"
|
||||
|
||||
#
|
||||
@@ -14,13 +14,27 @@ out_dir=/etc/freeradius
|
||||
|
||||
echo "Installing packages..."
|
||||
apt-get update
|
||||
apt-get install -y apt-transport-https ca-certificates curl software-properties-common
|
||||
apt-get install -y apt-transport-https ca-certificates curl
|
||||
|
||||
echo "Installing freeradius..."
|
||||
curl -fsSL http://connectone.pro/gpg | gpg --dearmor -o /usr/share/keyrings/connectone.gpg && \
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/connectone.gpg] \
|
||||
https://nexus.connectone.pro/repository/radius bionic main" | tee /etc/apt/sources.list.d/connectone.list \
|
||||
> /dev/null
|
||||
echo "Registering Gitea Debian registry..."
|
||||
DEB_REGISTRY="${DEB_REGISTRY:-https://git.ahax86.ru/api/packages/pub/debian}"
|
||||
DEB_DISTRIBUTION="${DEB_DISTRIBUTION:-noble}"
|
||||
DEB_COMPONENT="${DEB_COMPONENT:-main}"
|
||||
|
||||
install -d /etc/apt/keyrings
|
||||
curl -fsSL "${DEB_REGISTRY}/repository.key" -o /etc/apt/keyrings/gitea-pub.asc
|
||||
|
||||
apt_list=/etc/apt/sources.list.d/gitea-freeradius.list
|
||||
if [ -n "${DEB_AUTH:-}" ]; then
|
||||
# Private registry: embed "user:token" in the source URL (file kept root-only).
|
||||
apt_uri="${DEB_REGISTRY%%://*}://${DEB_AUTH}@${DEB_REGISTRY#*://}"
|
||||
echo "deb [signed-by=/etc/apt/keyrings/gitea-pub.asc] ${apt_uri} ${DEB_DISTRIBUTION} ${DEB_COMPONENT}" >"$apt_list"
|
||||
chmod 600 "$apt_list"
|
||||
else
|
||||
echo "deb [signed-by=/etc/apt/keyrings/gitea-pub.asc] ${DEB_REGISTRY} ${DEB_DISTRIBUTION} ${DEB_COMPONENT}" >"$apt_list"
|
||||
fi
|
||||
|
||||
echo "Installing freeradius ${FREERADIUS_TAG}..."
|
||||
apt-get update && apt-get install -y libfreeradius3=${FREERADIUS_TAG}+git freeradius-common=${FREERADIUS_TAG}+git \
|
||||
freeradius-postgresql=${FREERADIUS_TAG}+git freeradius-rest=${FREERADIUS_TAG}+git \
|
||||
freeradius-utils=${FREERADIUS_TAG}+git freeradius=${FREERADIUS_TAG}+git
|
||||
55
packaging/image/Dockerfile
Normal file
55
packaging/image/Dockerfile
Normal file
@@ -0,0 +1,55 @@
|
||||
# Runtime image for the custom FreeRADIUS build.
|
||||
# Installs the pre-built .deb packages from the Gitea Debian registry and bakes
|
||||
# the shared configs. Build context MUST be the repo root:
|
||||
# docker build -f packaging/image/Dockerfile -t <ref> .
|
||||
#
|
||||
# Version is pinned via PKG_VERSION (CI derives it from /VERSION, e.g. 3.2.8+git).
|
||||
FROM ubuntu:24.04
|
||||
WORKDIR /tmp
|
||||
ARG out_dir=/etc/freeradius
|
||||
ARG PKG_VERSION=3.2.8+git
|
||||
|
||||
# Gitea Debian package registry (override for a different instance/owner).
|
||||
ARG DEB_REGISTRY=https://git.ahax86.ru/api/packages/pub/debian
|
||||
ARG DEB_DISTRIBUTION=noble
|
||||
ARG DEB_COMPONENT=main
|
||||
|
||||
COPY packaging/image/local-noble.list /etc/apt/sources.list.d/ubuntu.sources
|
||||
RUN apt-get update && apt-get install -y apt-transport-https ca-certificates curl gpg
|
||||
|
||||
# Register the Gitea Debian registry (public read assumed for owner "pub").
|
||||
RUN install -d /etc/apt/keyrings && \
|
||||
curl -fsSL "${DEB_REGISTRY}/repository.key" -o /etc/apt/keyrings/gitea-pub.asc && \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/gitea-pub.asc] ${DEB_REGISTRY} ${DEB_DISTRIBUTION} ${DEB_COMPONENT}" \
|
||||
> /etc/apt/sources.list.d/gitea-freeradius.list
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libfreeradius3=${PKG_VERSION} freeradius-common=${PKG_VERSION} \
|
||||
freeradius-postgresql=${PKG_VERSION} freeradius-rest=${PKG_VERSION} \
|
||||
freeradius-utils=${PKG_VERSION} freeradius=${PKG_VERSION} && \
|
||||
apt autoremove --purge -y && apt-get clean && \
|
||||
rm -f /etc/apt/sources.list.d/gitea-freeradius.list
|
||||
|
||||
# Bake the shared configs (kept under packaging/hosted/config).
|
||||
COPY packaging/hosted/config /tmp/config
|
||||
RUN cp /tmp/config/radiusd.conf $out_dir/radiusd.conf && \
|
||||
cp /tmp/config/dictionary $out_dir/dictionary && \
|
||||
cp /tmp/config/clients.conf $out_dir/clients.conf && \
|
||||
cp /tmp/config/mods-enabled/raw $out_dir/mods-enabled/raw && \
|
||||
cp /tmp/config/sites-enabled/dynamic-clients $out_dir/sites-enabled/dynamic-clients && \
|
||||
rm -f $out_dir/sites-enabled/inner-tunnel && \
|
||||
rm -f $out_dir/mods-enabled/eap && \
|
||||
rm -f $out_dir/mods-enabled/proxy_rate_limit
|
||||
|
||||
# Enable delay_reject.
|
||||
RUN rm -f $out_dir/mods-enabled/delay && \
|
||||
ln -s $out_dir/mods-available/delay $out_dir/mods-enabled/delay
|
||||
|
||||
ADD packaging/image/entrypoint.sh /usr/bin/entrypoint.sh
|
||||
RUN chmod a+x /usr/bin/entrypoint.sh && chmod -R 750 $out_dir && chown -R freerad:root $out_dir && \
|
||||
mv /etc/freeradius/certs /tmp && \
|
||||
rm -rf /tmp/config
|
||||
|
||||
WORKDIR /etc/freeradius
|
||||
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
|
||||
CMD ["freeradius", "-f"]
|
||||
Reference in New Issue
Block a user