Isolate the Kratos admin API on a dedicated soauth docker network

The Kratos admin API is unauthenticated by design and relies on network
isolation, but so-kratos published 0.0.0.0:4434:4434 and daemon.json leaves
userland-proxy at its default of true. Docker therefore ran a proxy listener
on the host, and any container reached the admin API through the manager IP
or the bridge gateway regardless of its docker network.

so-kratos now sits alone on a new soauth network and no longer publishes
4434. 4433 is still published, so nginx is unchanged. so-soc is dual homed
and reaches the admin API over soauth, with publicHostUrl set explicitly.
so-user reaches it through docker exec, and wait_for_kratos polls the
container address instead of the host port.

firewall/iptables.jinja hardcoded sobridge in every generated DNAT, ACCEPT,
masquerade and isolation rule, so it is now driven by a docker:networks map
and a per-container networks list. Containers that do not declare one
default to sobridge, and .ip still resolves to the primary network's
address. soauth is only created on the roles that run so-kratos.

Setup already allows a custom docker range, so it now also prompts for the
auth network range and writes it to the docker pillar. Grids on the default
range need no decision during soup, since they get the same 172.17.2.0/24 a
fresh install would. Grids that set a custom range are prompted, defaulting
to the adjacent /24, and unattended upgrades take that default with a notice
rather than blocking.

so-kratos and so-soc are removed in up_to_3.4.0 so the highstate recreates
them with the correct network membership.
This commit is contained in:
Mike Reeves
2026-09-09 16:09:00 -04:00
parent c49008a413
commit cf3a4ebc27
15 changed files with 236 additions and 42 deletions
+20
View File
@@ -276,9 +276,20 @@ collect_dockernet() {
whiptail_invalid_input
whiptail_dockernet_sosnet "$DOCKERNET"
done
whiptail_authnet_sosnet "$(adjacent_net "$DOCKERNET")"
while ! valid_ip4 "$AUTHNET" || [[ $AUTHNET =~ "172.17.0." ]] || [[ "$AUTHNET" == "$DOCKERNET" ]]; do
whiptail_invalid_input
whiptail_authnet_sosnet "$AUTHNET"
done
fi
}
adjacent_net() {
echo "$1" | awk -F'.' '{ printf "%s.%s.%s.%s", $1, $2, ($3 + 1) % 256, $4 }'
}
collect_gateway() {
whiptail_management_interface_gateway
@@ -1399,6 +1410,15 @@ docker_pillar() {
"docker:"\
" range: '$DOCKERNET/24'"\
" gateway: '$DOCKERGATEWAY'" > $docker_pillar_file
if [ ! -z "$AUTHNET" ]; then
AUTHGATEWAY=$(echo $AUTHNET | awk -F'.' '{print $1,$2,$3,1}' OFS='.')
printf '%s\n'\
" networks:"\
" soauth:"\
" range: '$AUTHNET/24'"\
" gateway: '$AUTHGATEWAY'" >> $docker_pillar_file
fi
fi
}
+13
View File
@@ -365,6 +365,18 @@ whiptail_dockernet_sosnet() {
}
whiptail_authnet_sosnet() {
[ -n "$TESTING" ] && return
AUTHNET=$(whiptail --title "$whiptail_title" --inputbox \
"\nEnter a second /24 size network range WITHOUT the /24 suffix. The authentication services are isolated on their own network so that the identity provider is not reachable from other containers. It must not overlap the range you just entered, and any range within 172.17.0.0/24 cannot be used." 13 65 "$1" 3>&1 1>&2 2>&3)
local exitstatus=$?
whiptail_check_exitstatus $exitstatus
}
whiptail_end_settings() {
[ -n "$TESTING" ] && return
@@ -427,6 +439,7 @@ whiptail_end_settings() {
[[ -n $WEBUSER ]] && __append_end_msg "Web User: $WEBUSER"
[[ -n $DOCKERNET ]] && __append_end_msg "Docker network: $DOCKERNET/24"
[[ -n $AUTHNET ]] && __append_end_msg "Authentication network: $AUTHNET/24"
if [[ ${#ntp_servers[@]} -gt 0 ]]; then
__append_end_msg "NTP Servers:"
for server in "${ntp_servers[@]}"; do