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/
|
.git/
|
||||||
.idea/
|
.idea/
|
||||||
|
.gitea/
|
||||||
*.md
|
*.md
|
||||||
.kube/
|
charts/
|
||||||
build/
|
packaging/deb/
|
||||||
.kube/
|
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/
|
.idea/
|
||||||
pkg/
|
pkg/
|
||||||
bin/
|
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:
|
Packaging and delivery of a custom FreeRADIUS 3.2.x build for
|
||||||
```bash
|
hotspot / captive-portal scenarios. There is no application code here — the repo
|
||||||
cd build
|
builds upstream FreeRADIUS with the extra `rlm_raw` module and ships it to two
|
||||||
docker build . -t freerad_builder:3.2.x -f Dockerfile_build
|
runtimes (bare-metal and Kubernetes) via a Gitea-hosted registry.
|
||||||
docker run -e DEB_USER=${DEB_USER} DEB_PASS="${DEB_PASS}" freerad_builder:3.2.x ./upload.sh
|
|
||||||
|
## 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:
|
## Artifacts (Gitea, owner `pub`)
|
||||||
```bash
|
|
||||||
kubectl label node connect-prod-0 freeradius=enabled
|
| 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
|
The `.deb` is the prerequisite: `build-image` and `package-hosted` both `needs:
|
||||||
```bash
|
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
|
### CI setup (Gitea)
|
||||||
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
|
|
||||||
|
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
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}-configs
|
name: {{ include "freeradius.fullname" . }}-configs
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
labels:
|
labels:
|
||||||
app: clickhouse-operator
|
{{- include "freeradius.labels" . | nindent 4 }}
|
||||||
data:
|
data:
|
||||||
default: |
|
default: |
|
||||||
server default {
|
server default {
|
||||||
listen {
|
listen {
|
||||||
type = auth
|
type = auth
|
||||||
ipaddr = *
|
ipaddr = *
|
||||||
port = {{.Values.ports.main}}
|
port = {{ .Values.service.authPort }}
|
||||||
limit {
|
limit {
|
||||||
max_connections = 16
|
max_connections = 16
|
||||||
lifetime = 0
|
lifetime = 0
|
||||||
@@ -21,7 +21,7 @@ data:
|
|||||||
|
|
||||||
listen {
|
listen {
|
||||||
ipaddr = *
|
ipaddr = *
|
||||||
port = {{.Values.ports.second}}
|
port = {{ .Values.service.acctPort }}
|
||||||
type = acct
|
type = acct
|
||||||
|
|
||||||
limit {
|
limit {
|
||||||
@@ -99,7 +99,6 @@ data:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sql: |
|
sql: |
|
||||||
sql {
|
sql {
|
||||||
dialect = "postgresql"
|
dialect = "postgresql"
|
||||||
@@ -111,7 +110,7 @@ data:
|
|||||||
server = "{{ .Values.db.host }}"
|
server = "{{ .Values.db.host }}"
|
||||||
port = {{ .Values.db.port }}
|
port = {{ .Values.db.port }}
|
||||||
login = "{{ .Values.db.user }}"
|
login = "{{ .Values.db.user }}"
|
||||||
password = "{{.Values.db.password}}"
|
password = "$ENV{DB_PASSWORD}"
|
||||||
radius_db = "{{ .Values.db.name }}"
|
radius_db = "{{ .Values.db.name }}"
|
||||||
|
|
||||||
acct_table1 = "radacct"
|
acct_table1 = "radacct"
|
||||||
@@ -167,4 +166,3 @@ data:
|
|||||||
idle_timeout = 60
|
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
|
RUN apt-get update && apt-get install -y devscripts equivs git quilt gcc
|
||||||
|
|
||||||
#
|
#
|
||||||
# Setup build envs
|
# Setup build envs.
|
||||||
ARG RADIUS_TAG="release_3_2_5"
|
# 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"
|
ARG RLM_RAW_BRANCH="v3.2.4"
|
||||||
|
|
||||||
ENV RADIUS_REPO="https://github.com/FreeRADIUS/freeradius-server.git"
|
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,
|
# If you wish to disable this pre-caching and reachability check,
|
||||||
# comment out the configuration item below.
|
# comment out the configuration item below.
|
||||||
#connect_uri = "http://connectone.me:31668"
|
|
||||||
connect_uri = "http://#API_HOST#:#API_PORT#/"
|
connect_uri = "http://#API_HOST#:#API_PORT#/"
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -14,13 +14,27 @@ out_dir=/etc/freeradius
|
|||||||
|
|
||||||
echo "Installing packages..."
|
echo "Installing packages..."
|
||||||
apt-get update
|
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..."
|
echo "Registering Gitea Debian registry..."
|
||||||
curl -fsSL http://connectone.pro/gpg | gpg --dearmor -o /usr/share/keyrings/connectone.gpg && \
|
DEB_REGISTRY="${DEB_REGISTRY:-https://git.ahax86.ru/api/packages/pub/debian}"
|
||||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/connectone.gpg] \
|
DEB_DISTRIBUTION="${DEB_DISTRIBUTION:-noble}"
|
||||||
https://nexus.connectone.pro/repository/radius bionic main" | tee /etc/apt/sources.list.d/connectone.list \
|
DEB_COMPONENT="${DEB_COMPONENT:-main}"
|
||||||
> /dev/null
|
|
||||||
|
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 \
|
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-postgresql=${FREERADIUS_TAG}+git freeradius-rest=${FREERADIUS_TAG}+git \
|
||||||
freeradius-utils=${FREERADIUS_TAG}+git freeradius=${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