commit 547ef493bab257fbb193a21bbd005b2569d82491 Author: rohhie Date: Sun Oct 30 10:59:26 2022 +0900 First version diff --git a/DockerfileIPv4 b/DockerfileIPv4 new file mode 100644 index 0000000..1b5d32a --- /dev/null +++ b/DockerfileIPv4 @@ -0,0 +1,9 @@ +FROM alpine:latest +RUN apk add dhcp-server-vanilla samba-dc krb5 bash bind-tools tzdata && \ + touch /var/lib/dhcp/dhcpd.leases +ADD v4/entrypoint.sh / +ADD v4/dhcpd.conf /etc/dhcp/ +ADD dhcp-dyndns.sh /usr/local/bin/ +ADD ssoauth.keytab /etc/ +ADD krb5.conf /etc/ +ENTRYPOINT ["/entrypoint.sh"] diff --git a/DockerfileIPv6 b/DockerfileIPv6 new file mode 100644 index 0000000..b4ad2e0 --- /dev/null +++ b/DockerfileIPv6 @@ -0,0 +1,11 @@ +FROM alpine:latest +RUN apk add dhcp-server-vanilla radvd samba-dc krb5 bash bind-tools tzdata && \ + touch /var/lib/dhcp/dhcpd.leases && \ + mkdir /run/radvd +ADD v6/entrypoint.sh / +ADD v6/dhcpd.conf /etc/dhcp/ +ADD v6/radvd.conf /etc/ +ADD dhcp-dyndns.sh /usr/local/bin/ +ADD ssoauth.keytab /etc/ +ADD krb5.conf /etc/ +ENTRYPOINT ["/entrypoint.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cdc6638 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2022 rohhie@rohhie.net + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSE_dhcp-dyndns.sh b/LICENSE_dhcp-dyndns.sh new file mode 100644 index 0000000..cc0f37c --- /dev/null +++ b/LICENSE_dhcp-dyndns.sh @@ -0,0 +1,44 @@ +# Version: 0.9.5 +# +# Copyright (C) Rowland Penny 2020-2022 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# --- +# +# Revised 2022-10-22, Rohhie, as follows: +# - logs to stderr. +# - Server name, domain name and DNS admin name are now specified in +# environment variables. +# - Since the assumption is that the user has been created, +# the test logic was removed. +# - Moved the parameter "-s" in the klist command. +# - When expired, the host name is retrieved from the target IP address. +# - Added processing to support IPv6 +# (works as far as I want to use it in my home lab). +# +# Copyright (C) rohhie@rohhie.net 2022 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c35e98 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Samba-ad-dc-ddns-docker + +## 概要 + +Samba ad dcと連携するDHCPサーバーをDockerで気軽に利用する。 + +## 構築方法 +### ダウンロード + +このリポジトリからソースをダウンロードする。 + +``` +git clone https://gitea.rohhie.net/rohhie/samba-ad-dc-ddns-docker.git ddns +cd ddns +``` + +### 動作条件を設定 +#### コンテナ + +docker-compose.ymlには、IPv4とIPv6の2つのコンテナを定義してある。 +どちらか一方を利用する場合は、使わない方をコメントアウトしておく。 + +それぞれの動作条件を設定する。 + +| 変数名 | 設定内容 | +|------------|--------------------------------------------------------------------------| +| DHCPIF | 使用するネットワークインターフェースを設定。空白で区切って複数指定可能。 | +| DHCPSERVER | Samba AD DCのホスト名を指定。名前解決できる(はず)。 | +| DHCPDOMAIN | ドメインのFQDNを指定。 | +| DHCPDNSADM | DnsAdminsのユーザー名を指定。 | +| DHCPDBGFLG | "true"を指定すると、標準出力にデバッグ情報が出力される。 | + +#### IPv4 + +v4ディレクトリにdhcpd.confがあるので、これを適宜書き換える。 + +#### IPv6 + +v6ディレクトリにdhcpd.confとradvd.confがあるので、適切に設定。 +radvd.confでは、使用するネットワークインターフェースの設定があるので、環境変数DHCPIFとあわせて設定しておく。 + +#### Kerberos + +krb5.confをSamba ad dcにアクセスできるように作成。 + +#### keytab + +Samba ad dcでDnsAdminに所属するユーザーのkeytabを取り出す。 +ホームラボでは、Samba ad dc立ち上げ時にssoauthというユーザーを作りDnsAdminにしたので、これを使っている。 +新たにユーザーを作っても良いし、既存のユーザーを使っても良い。 +``` +.keytab +``` +という名前で保管しておく。 +ホームラボではssoauth.keytabという名前で保管している。 + +#### dhcp-dyndns.sh + +これは、Samba Wiki(https://wiki.samba.org/index.php/Configure_DHCP_to_update_DNS_records)にあるスクリプトをベースにして、IPv6の処理を追加している。 +ユーザーはssoauthとしているので、DnsAdminのユーザー名に置換して使用する。 + +### ファイアウォールの設定 + +ファイアウォールを設定するスクリプトを実行する。 +このスクリプトではIPv6のみ開放している。 +IPv4は開放しなくてもうまく動いている。 + +``` +sudo ./setufw.sh +``` + +何らかの理由で設定を解除するなら、以下を実行する。 + +``` +sudo ./setufw.sh delete +``` + +### コンテナを起動 + +コンテナを構築して起動する。 + +``` +sudo docker compose up -d --bulid +``` + +## その他 + +細かな設定手順や使い方は、メインサイト参照。 +https://rohhie.net/samba-ad-dc-dynamicdns/ + +## ライセンス + +MIT + +ただし、dhcp-dyndns.shだけはソースコード参照。 +変更点は原作に準じてGPL3.0以降とする。 diff --git a/dhcp-dyndns.sh b/dhcp-dyndns.sh new file mode 100755 index 0000000..5d6b056 --- /dev/null +++ b/dhcp-dyndns.sh @@ -0,0 +1,509 @@ +#!/bin/bash +# On FreeBSD change the above line to #!/usr/local/bin/bash +# +# /usr/local/bin/dhcp-dyndns.sh +# +# This script is for secure DDNS updates on Samba, +# it can also add the 'macAddress' to the Computers object. +# +# Version: 0.9.5 +# +# Copyright (C) Rowland Penny 2020-2022 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Revised 2022-10-22, Rohhie, as follows: +# - ログをstderrに出力するようにした。 +# - サーバー名とドメイン名、DNS管理者ユーザー名を環境変数で指定するようにした。 +# - ユーザーは作ってある前提なのでテストロジックを削除した。 +# - klistの-s指定をファイル名の前に移動した。 +# - 期限切れで削除になる時、対象IPアドレスからホスト名を取得するようにした。 +# - IPv6に対応する処理を追加した(ホームラボで使いたい範囲で動く程度)。 + +# You may need to ensure that you have a useful path +# If you have 'path' problems, Uncomment the next line and adjust for +# your setup e.g. self-compiled Samba +#export PATH=/usr/local/samba/bin:/usr/local/samba/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin + +########################################################################## +# # +# You can optionally add the 'macAddress' to the Computers object. # +# Add '$DHCPDNSADM' to the 'Domain Admins' group if used # +# Change the next line to 'yes' to make this happen # +Add_macAddress='no' +# # +########################################################################## + +# On FreeBSD change this to /usr/local/etc/.keytab +keytab=/etc/$DHCPDNSADM.keytab + +usage() +{ + cat <<-EOF + USAGE: + $(basename "$0") add ip-address dhcid|mac-address hostname + $(basename "$0") delete ip-address dhcid|mac-address + EOF +} + +_KERBEROS() +{ + # get current time as a number + test=$(date +%d'-'%m'-'%y' '%H':'%M':'%S) + # Note: there have been problems with this + # check that 'date' returns something like + + # Check for valid kerberos ticket + #logger -s "${test} [dyndns] : Running check for valid kerberos ticket" + klist -c -s "${KRB5CCNAME}" + ret="$?" + if [ $ret -ne 0 ] + then + logger -s "${test} [dyndns] : Getting new ticket, old one has expired" + # On FreeBSD change the -F to --no-forwardable + kinit -F -k -t $keytab "${SETPRINCIPAL}" + ret="$?" + if [ $ret -ne 0 ] + then + logger -s "${test} [dyndns] : dhcpd kinit for dynamic DNS failed" + exit 1 + fi + fi +} + +rev_zone_info() +{ + local RevZone="$1" + local IP="$2" + local rzoneip + local rzonenum + + if [[ $RevZone =~ .+"in-addr.arpa"$ ]]; then + if [ $3 = "AAAA" ]; then + ZoneIP="-no match-" + return; + fi + rzoneip="${RevZone%.in-addr.arpa}" + else + if [ $3 = "A" ]; then + ZoneIP="-no match-" + return; + fi + rzoneip="${RevZone%.ip6.arpa}" + fi + + unset ZoneIP + unset RZIP + unset IP2add + + if [ $3 = "A" ]; then + rzonenum=$(echo "$rzoneip" | tr '.' '\n') + declare -a words + for n in $rzonenum + do + words+=("$n") + done + local numwords="${#words[@]}" + + case "$numwords" in + 1) + # single ip rev zone '192' + ZoneIP=$(echo "${IP}" | awk -F '.' '{print $1}') + RZIP="${rzoneip}" + IP2add=$(echo "${IP}" | awk -F '.' '{print $4"."$3"."$2}') + ;; + 2) + # double ip rev zone '168.192' + ZoneIP=$(echo "${IP}" | awk -F '.' '{print $1"."$2}') + RZIP=$(echo "${rzoneip}" | awk -F '.' '{print $2"."$1}') + IP2add=$(echo "${IP}" | awk -F '.' '{print $4"."$3}') + ;; + 3) + # triple ip rev zone '0.168.192' + ZoneIP=$(echo "${IP}" | awk -F '.' '{print $1"."$2"."$3}') + RZIP=$(echo "${rzoneip}" | awk -F '.' '{print $3"."$2"."$1}') + IP2add=$(echo "${IP}" | awk -F '.' '{print $4}') + ;; + *) + # should never happen + exit 1 + ;; + esac + else + local v6ip=$(echo -n $2 | sed "s/://g") + local v6zn=$(echo -n $rzoneip | sed "s/\.//g" | rev) + if [[ $v6ip =~ "$v6zn".+ ]]; then + ZoneIP=$2 + RZIP="${ZoneIP}" + IP2add=$(echo -n $v6ip | sed "s/$v6zn\(.\+\)/\1/" | rev | sed "s/\(.\)/\1./g" | sed "s/.$//") + rzoneip="${RevZone%.ip6.arpa}" + else + ZoneIP="-no match-" + return; + fi + fi +} + +ipv6_short_to_long () { + divaddr=$(echo -n $1 | sed "s/::/:-:/") + local IFSBAK=$IFS + IFS=: divaddr=($divaddr) + IFS=$IFSBAK + + defaddr=() + for element in "${divaddr[@]}"; do + if [ $element = "-" ]; then + padmax=$((8-${#divaddr[@]})) + for (( i=0; i<=$padmax; i++ )) + do + defaddr+=("0000") + done + else + padding="0000${element}" + defaddr+=(${padding: -4}) + fi + done + + longaddr=$(echo -n ${defaddr[@]} | sed "s/ /:/g") + + echo $longaddr +} + +BINDIR=$(samba -b | grep 'BINDIR' | grep -v 'SBINDIR' | awk '{print $NF}') +[[ -z $BINDIR ]] && printf "Cannot find the 'samba' binary, is it installed ?\\nOr is your path set correctly ?\\n" +WBINFO="$BINDIR/wbinfo" + +SAMBATOOL=$(command -v samba-tool) +[[ -z $SAMBATOOL ]] && printf "Cannot find the 'samba-tool' binary, is it installed ?\\nOr is your path set correctly ?\\n" + +MINVER=$($SAMBATOOL -V | grep -o '[0-9]*' | tr '\n' ' ' | awk '{print $2}') +if [ "$MINVER" -gt '14' ] +then + KTYPE="--use-kerberos=required" +else + KTYPE="-k yes" +fi + +# DHCP Server hostname +#Server=$(hostname -s) +Server=${DHCPSERVER}.${DHCPDOMAIN} + +# DNS domain +#domain=$(hostname -d) +domain=${DHCPDOMAIN} +if [ -z "${domain}" ] +then + logger -s "Cannot obtain domain name, is DNS set up correctly?" + logger -s "Cannot continue... Exiting." + exit 1 +fi + +# Samba realm +REALM="${domain^^}" + +# krbcc ticket cache +export KRB5CCNAME="/tmp/dhcp-dyndns.cc" + +# Kerberos principal +SETPRINCIPAL="${DHCPDNSADM}@${REALM}" +# Kerberos keytab as above +# krbcc ticket cache : /tmp/dhcp-dyndns.cc +#TESTUSER="$($WBINFO -u | grep $DHCPDNSADM)" +#if [ -z "${TESTUSER}" ] +#then +# logger -s "No AD dhcp user exists, need to create it first.. exiting." +# logger -s "you can do this by typing the following commands" +# logger -s "kinit Administrator@${REALM}" +# logger -s "$SAMBATOOL user create $DHCPDNSADM --random-password --description='Unprivileged user for DNS updates via ISC DHCP server'" +# logger -s "$SAMBATOOL user setexpiry $DHCPDNSADM --noexpiry" +# logger -s "$SAMBATOOL group addmembers DnsAdmins $DHCPDNSADM" +# exit 1 +#fi + +# Check for Kerberos keytab +if [ ! -f /etc/$DHCPDNSADM.keytab ] +then + logger -s "Required keytab $keytab not found, it needs to be created." + logger -s "Use the following commands as root" + logger -s "$SAMBATOOL domain exportkeytab --principal=${SETPRINCIPAL} $keytab" + logger -s "chown XXXX:XXXX $keytab" + logger -s "Replace 'XXXX:XXXX' with the user & group that dhcpd runs as on your distro" + logger -s "chmod 400 $keytab" + exit 1 +fi + +# Variables supplied by dhcpd.conf +action="$1" +DHCID="$3" +name="${4%%.*}" +if [ "$5" = "-IPv6-" ]; then + ip=$(ipv6_short_to_long $2) + AorAAAA="AAAA" +else + ip="$2" + AorAAAA="A" +fi + +# Exit if no ip address +if [ -z "${ip}" ] +then + usage + exit 1 +fi + +# Exit if no computer name supplied, unless the action is 'delete' +if [ -z "${name}" ] +then + if [ "${action}" = "delete" ] + then + name=$(host -t PTR "${ip}" | awk '{print $NF}' | awk -F '.' '{print $1}') + else + usage + exit 1 + fi +fi + +# exit if name contains a space +case ${name} in + *\ * ) + logger -s "Invalid hostname '${name}' ...Exiting" + exit + ;; +esac + +# if you want computers with a hostname that starts with 'dhcp' in AD +# comment the following block of code. +if [[ $name == dhcp* ]] +then + logger -s "not updating DNS record in AD, invalid name" + exit 0 +fi + +## update ## +case "${action}" in + add) + _KERBEROS + count=0 + # does host have an existing 'A' record ? + A_REC=$($SAMBATOOL dns query "${Server}" "${domain}" "${name}" $AorAAAA "$KTYPE" 2>/dev/null | grep "$AorAAAA:" | awk '{print $2}') + # turn A_REC into an array + A_REC=("$A_REC") + if [ "${#A_REC[@]}" -eq 0 ] + then + # no A record to delete + result1=0 + $SAMBATOOL dns add "${Server}" "${domain}" "${name}" $AorAAAA "${ip}" "$KTYPE" + result2="$?" + elif [ "${#A_REC[@]}" -gt 1 ] + then + for i in "${A_REC[@]}" + do + $SAMBATOOL dns delete "${Server}" "${domain}" "${name}" $AorAAAA "${i}" "$KTYPE" + done + # all A records deleted + result1=0 + $SAMBATOOL dns add "${Server}" "${domain}" "${name}" $AorAAAA "${ip}" "$KTYPE" + result2="$?" + elif [ "${#A_REC[@]}" -eq 1 ] + then + # turn array into a variable + VAR_A_REC="${A_REC[*]}" + if [ "$VAR_A_REC" = "${ip}" ] + then + # Correct A record exists, do nothing + logger -s "Correct '$AorAAAA' record exists, not updating." + result1=0 + result2=0 + count=$((count+1)) + elif [ "$VAR_A_REC" != "${ip}" ] + then + # Wrong A record exists + logger -s "'$AorAAAA' record changed, updating record." + $SAMBATOOL dns delete "${Server}" "${domain}" "${name}" $AorAAAA "${VAR_A_REC}" "$KTYPE" + result1="$?" + $SAMBATOOL dns add "${Server}" "${domain}" "${name}" $AorAAAA "${ip}" "$KTYPE" + result2="$?" + fi + fi + + # get existing reverse zones (if any) + ReverseZones=$($SAMBATOOL dns zonelist "${Server}" "$KTYPE" --reverse | grep 'pszZoneName' | awk '{print $NF}') + if [ -z "$ReverseZones" ]; then + logger -s "No reverse zone found, not updating" + result3='0' + result4='0' + count=$((count+1)) + else + for revzone in $ReverseZones + do + rev_zone_info "$revzone" "${ip}" "${AorAAAA}" + if [[ ${ip} = $ZoneIP* ]] && [ "$ZoneIP" = "$RZIP" ] + then + # does host have an existing 'PTR' record ? + PTR_REC=$($SAMBATOOL dns query "${Server}" "${revzone}" "${IP2add}" PTR "$KTYPE" 2>/dev/null | grep 'PTR:' | awk '{print $2}' | awk -F '.' '{print $1}') + if [[ -z $PTR_REC ]] + then + # no PTR record to delete + result3=0 + $SAMBATOOL dns add "${Server}" "${revzone}" "${IP2add}" PTR "${name}"."${domain}" "$KTYPE" + result4="$?" + break + elif [ "$PTR_REC" = "${name}" ] + then + # Correct PTR record exists, do nothing + logger -s "Correct 'PTR' record exists, not updating." + result3=0 + result4=0 + count=$((count+1)) + break + elif [ "$PTR_REC" != "${name}" ] + then + # Wrong PTR record exists + # points to wrong host + logger -s "'PTR' record changed, updating record." + $SAMBATOOL dns delete "${Server}" "${revzone}" "${IP2add}" PTR "${PTR_REC}"."${domain}" "$KTYPE" + result3="$?" + $SAMBATOOL dns add "${Server}" "${revzone}" "${IP2add}" PTR "${name}"."${domain}" "$KTYPE" + result4="$?" + break + fi + else + continue + fi + done + fi + ;; + delete) + _KERBEROS + + count=0 + name=$(host -t PTR "${ip}" | sed 's/.\+pointer \([^.]\+\).\+$/\1/') + $SAMBATOOL dns delete "${Server}" "${domain}" "${name}" $AorAAAA "${ip}" "$KTYPE" + result1="$?" + # get existing reverse zones (if any) + ReverseZones=$($SAMBATOOL dns zonelist "${Server}" --reverse "$KTYPE" | grep 'pszZoneName' | awk '{print $NF}') + if [ -z "$ReverseZones" ] + then + logger -s "No reverse zone found, not updating" + result2='0' + count=$((count+1)) + else + for revzone in $ReverseZones + do + rev_zone_info "$revzone" "${ip}" "${AorAAAA}" + if [[ ${ip} = $ZoneIP* ]] && [ "$ZoneIP" = "$RZIP" ] + then + host -t PTR "${ip}" > /dev/null 2>&1 + ret="$?" + if [ $ret -eq 0 ] + then + $SAMBATOOL dns delete "${Server}" "${revzone}" "${IP2add}" PTR "${name}"."${domain}" "$KTYPE" + result2="$?" + else + result2='0' + count=$((count+1)) + fi + break + else + continue + fi + done + fi + result3='0' + result4='0' + ;; + *) + logger -s "Invalid action specified" + exit 103 + ;; +esac + +result="${result1}:${result2}:${result3}:${result4}" + +if [ "$count" -eq 0 ] +then + if [ "${result}" != "0:0:0:0" ] + then + logger -s "DHCP-DNS $action failed: ${result}" + exit 1 + else + logger -s "DHCP-DNS $action succeeded" + fi +fi + +if [ "$Add_macAddress" != 'no' ] +then + if [ -n "$DHCID" ] + then + Computer_Object=$(ldbsearch "$KTYPE" -H ldap://"$Server" "(&(objectclass=computer)(objectclass=ieee802Device)(cn=$name))" | grep -v '#' | grep -v 'ref:') + if [ -z "$Computer_Object" ] + then + # Computer object not found with the 'ieee802Device' objectclass, does the computer actually exist, it should. + Computer_Object=$(ldbsearch "$KTYPE" -H ldap://"$Server" "(&(objectclass=computer)(cn=$name))" | grep -v '#' | grep -v 'ref:') + if [ -z "$Computer_Object" ] + then + logger -s "Computer '$name' not found. Exiting." + exit 68 + else + DN=$(echo "$Computer_Object" | grep 'dn:') + objldif="$DN +changetype: modify +add: objectclass +objectclass: ieee802Device" + + attrldif="$DN +changetype: modify +add: macAddress +macAddress: $DHCID" + + # add the ldif + echo "$objldif" | ldbmodify "$KTYPE" -H ldap://"$Server" + ret="$?" + if [ $ret -ne 0 ] + then + logger -s "Error modifying Computer objectclass $name in AD." + exit "${ret}" + fi + sleep 2 + echo "$attrldif" | ldbmodify "$KTYPE" -H ldap://"$Server" + ret="$?" + if [ "$ret" -ne 0 ]; then + logger -s "Error modifying Computer attribute $name in AD." + exit "${ret}" + fi + unset objldif + unset attrldif + logger -s "Successfully modified Computer $name in AD" + fi + else + DN=$(echo "$Computer_Object" | grep 'dn:') + attrldif="$DN +changetype: modify +replace: macAddress +macAddress: $DHCID" + + echo "$attrldif" | ldbmodify "$KTYPE" -H ldap://"$Server" + ret="$?" + if [ "$ret" -ne 0 ] + then + logger -s "Error modifying Computer attribute $name in AD." + exit "${ret}" + fi + unset attrldif + logger -s "Successfully modified Computer $name in AD" + fi + fi +fi + +exit 0 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..bc4e3d6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,44 @@ +version: "3.9" +services: + + dhcp4: + build: + context: ./ + dockerfile: DockerfileIPv4 + image: dhcp4:1.0.0 + container_name: dhcp4 + restart: "unless-stopped" + environment: + TZ: Asia/Tokyo + DHCPIF: "ens33" + DHCPSERVER: "addc" + DHCPDOMAIN: "example.net" + DHCPDNSADM: "ssoauth" + DHCPDBGFLG: "false" + hostname: dhcp4 + network_mode: "host" + volumes: + - v4:/var/lib/dhcp + + dhcp6: + build: + context: ./ + dockerfile: DockerfileIPv6 + image: dhcp6:1.0.0 + container_name: dhcp6 + restart: "unless-stopped" + environment: + TZ: Asia/Tokyo + DHCPIF: "ens33" + DHCPSERVER: "addc" + DHCPDOMAIN: "example.net" + DHCPDNSADM: "ssoauth" + DHCPDBGFLG: "false" + hostname: dhcp6 + network_mode: "host" + volumes: + - v6:/var/lib/dhcp + +volumes: + v4: + v6: diff --git a/krb5.conf b/krb5.conf new file mode 100644 index 0000000..e8fd050 --- /dev/null +++ b/krb5.conf @@ -0,0 +1,14 @@ +[libdefaults] + default_realm = EXAMPLE.NET + dns_lookup_realm = false + dns_lookup_kdc = true + +[realms] +EXAMPLE.NET = { + kdc = addc.example.net +# kdc = addc2.example.net + default_domain = example.net +} + +[domain_realm] + addc = EXAMPLE.NET diff --git a/setufw.sh b/setufw.sh new file mode 100755 index 0000000..a4e048a --- /dev/null +++ b/setufw.sh @@ -0,0 +1,2 @@ +#!/bin/bash +ufw $1 allow to any port 547 proto udp from any comment "DHCPv6" diff --git a/v4/dhcpd.conf b/v4/dhcpd.conf new file mode 100644 index 0000000..44174ce --- /dev/null +++ b/v4/dhcpd.conf @@ -0,0 +1,75 @@ +#------------------------------- +# Global options +#------------------------------- +authoritative; + +option domain-name "example.net"; +option domain-name-servers 192.168.110.10; +option routers 192.168.110.10; + +default-lease-time 86400; # 24 hours. +max-lease-time 604800; # 7 days. + +lease-file-name "/var/lib/dhcp/dhcpd.leases"; + +#------------------------------- +# Subnet +#------------------------------- +subnet 192.168.110.0 netmask 255.255.255.0 { + range 192.168.110.100 192.168.110.199; +} + +#------------------------------- +# Fixed address +#------------------------------- +host party { + hardware ethernet 00:0C:29:14:6E:24; + fixed-address 192.168.110.12; +} + +host work { + hardware ethernet 00:0c:29:0a:83:af; + fixed-address 192.168.110.3; +} + +#------------------------------- +# Dynamic DNS +#------------------------------- +on commit { + set noname = concat("dhcp-", binary-to-ascii(10, 8, "-", leased-address)); + set ClientIP = binary-to-ascii(10, 8, ".", leased-address); + set ClientDHCID = concat ( + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2) + ); + set ClientName = pick-first-value(option host-name, config-option host-name, client-name, noname); + #log(concat("Commit: IP: ", ClientIP, " DHCID: ", ClientDHCID, " Name: ", ClientName)); + execute("/usr/local/bin/dhcp-dyndns.sh", "add", ClientIP, ClientDHCID, ClientName); +} + +on release { + set ClientIP = binary-to-ascii(10, 8, ".", leased-address); + set ClientDHCID = concat ( + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":", + suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2) + ); + #log(concat("Release: IP: ", ClientIP)); + execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, ClientDHCID); +} + +on expiry { + set ClientIP = binary-to-ascii(10, 8, ".", leased-address); + # cannot get a ClientMac here, apparently this only works when actually receiving a packet + #log(concat("Expired: IP: ", ClientIP)); + # cannot get a ClientName here, for some reason that always fails + # however the dhcp update script will obtain the short hostname. + execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "", "0"); +} diff --git a/v4/entrypoint.sh b/v4/entrypoint.sh new file mode 100755 index 0000000..29dcecb --- /dev/null +++ b/v4/entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +echo "Start container with parameter : $@" + +trap sig_term SIGTERM + +sig_term() { + echo "CATCH SIGTERM" + pkill -SIGTERM dhcpd + wait + exit 0 +} + +# Execute paramater. +exec "$@" + +if [ ${DHCPDBGFLG^^} = "TRUE" ]; then + DBG="-d" +fi + +dhcpd -4 $DBG -f $DHCPIF & +wait +exit 1 diff --git a/v6/dhcpd.conf b/v6/dhcpd.conf new file mode 100644 index 0000000..fa13e50 --- /dev/null +++ b/v6/dhcpd.conf @@ -0,0 +1,51 @@ +#------------------------------- +# Global options +#------------------------------- +authoritative; + +option dhcp6.domain-search "example.net"; +option dhcp6.name-servers fdaa:aaaa:aaaa:aaaa::10; + +default-lease-time 86400; # 24 hours. +max-lease-time 604800; # 7 days. + +lease-file-name "/var/lib/dhcp/dhcpd.leases"; + +#log-facility syslog; + +#------------------------------- +# Subnet +#------------------------------- +subnet6 fdaa:aaaa:aaaa:aaaa::/64 { + range6 fdaa:aaaa:aaaa:aaaa::1:0100 fdaa:aaaa:aaaa:aaaa::1:0199; +} + +#------------------------------- +# Fixed address +#------------------------------- +host party { + host-identifier option dhcp6.client-id 00:03:00:01:00:0c:29:14:6e:24; + fixed-address6 fdaa:aaaa:aaaa:aaaa::12; + on commit { execute("/usr/local/bin/dhcp-dyndns.sh", "add", "fdaa:aaaa:aaaa:aaaa::12", "no", "party", "-IPv6-"); } + on release { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", "fdaa:aaaa:aaaa:aaaa::12", "no", "party", "-IPv6-"); } + on expiry { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", "fdaa:aaaa:aaaa:aaaa::12", "no", "party", "-IPv6-"); } +} + +host work { + host-identifier option dhcp6.client-id 00:03:00:01:00:0c:29:0a:83:af; + fixed-address6 fdaa:aaaa:aaaa:aaaa::3; + on commit { execute("/usr/local/bin/dhcp-dyndns.sh", "add", "fdaa:aaaa:aaaa:aaaa::3", "no", "work", "-IPv6-"); } + on release { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", "fdaa:aaaa:aaaa:aaaa::3", "no", "work", "-IPv6-"); } + on expiry { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", "fdaa:aaaa:aaaa:aaaa::3", "no", "work", "-IPv6-"); } +} + +#------------------------------- +# Dynamic DNS +#------------------------------- +on commit { + set ClientIP = binary-to-ascii(16, 16, ":", substring(option dhcp6.ia-na, 16, 16)); + set ClientName = option fqdn.hostname; + execute("/usr/local/bin/dhcp-dyndns.sh", "add", ClientIP, "no", ClientName, "-IPv6-"); +} +on release { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "no", ClientName, "-IPv6-"); } +on expiry { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "no", ClientName, "-IPv6-"); } diff --git a/v6/entrypoint.sh b/v6/entrypoint.sh new file mode 100755 index 0000000..e56a29f --- /dev/null +++ b/v6/entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +echo "Start container with parameter : $@" + +trap sig_term SIGTERM + +sig_term() { + echo "CATCH SIGTERM" + pkill -SIGTERM dhcpd + pkill -SIGTERM radvd + wait + exit 0 +} + +# Execute paramater. +exec "$@" + +if [ ${DHCPDBGFLG^^} = "TRUE" ]; then + DBG=-d +fi + +dhcpd -6 $DBG -f $DHCPIF & +radvd -n & +wait diff --git a/v6/radvd.conf b/v6/radvd.conf new file mode 100644 index 0000000..896010b --- /dev/null +++ b/v6/radvd.conf @@ -0,0 +1,16 @@ +interface ens33 +{ + AdvSendAdvert on; + + AdvManagedFlag on; + AdvOtherConfigFlag on; + + AdvDefaultPreference low; + + prefix fdaa:aaaa:aaaa:aaaa::/64 + { + AdvAutonomous off; + }; + + AdvCurHopLimit 0; +};