Skip to content

Instantly share code, notes, and snippets.

@n1dr00
Forked from Nockiro/dhcp-dyndns.sh
Created October 10, 2024 19:24
Show Gist options
  • Save n1dr00/20d83e5419c2cb5752c9f1486d8ebd72 to your computer and use it in GitHub Desktop.
Save n1dr00/20d83e5419c2cb5752c9f1486d8ebd72 to your computer and use it in GitHub Desktop.

Revisions

  1. @Nockiro Nockiro revised this gist Mar 7, 2021. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions dhcp-dyndns.sh
    Original file line number Diff line number Diff line change
    @@ -51,7 +51,7 @@ _KERBEROS () {
    klist -c "${KRB5CCNAME}" -s
    if [ "$?" != "0" ]; then
    logger "${test} [dyndns] : Getting new ticket, old one has expired"
    if [[ "$OSTYPE" == "FreeBSD" ]]; then
    if [[ "$OSTYPE" == "freebsd"* ]]; then
    kinit --no-forwardable -k -t /etc/dhcpduser.keytab "${SETPRINCIPAL}"
    else
    kinit -F -k -t /etc/dhcpduser.keytab "${SETPRINCIPAL}"
    @@ -100,12 +100,12 @@ BINDIR=$(samba -b | grep 'BINDIR' | grep -v 'SBINDIR' | awk '{print $NF}')
    WBINFO="$BINDIR/wbinfo"

    LDBCOMMAND="ldbsearch"
    if [[ "$OSTYPE" == "FreeBSD" ]]; then
    if [[ "$OSTYPE" == "freebsd"* ]]; then
    LDBCOMMAND="samba-ldbsearch"
    fi

    LDBCOMMANDMOD="ldbmodify"
    if [[ "$OSTYPE" == "FreeBSD" ]]; then
    if [[ "$OSTYPE" == "freebsd"* ]]; then
    LDBCOMMANDMOD="samba-ldbmodify"
    fi

    @@ -124,7 +124,7 @@ fi
    REALM=$(echo ${domain^^})

    # Samba SAM file - can also be ldap://${SERVER}, but that could get very slow
    if [[ "$OSTYPE" == "FreeBSD" ]]; then
    if [[ "$OSTYPE" == "freebsd"* ]]; then
    DATABASE="/var/db/samba4/private/sam.ldb"
    else
    DATABASE="/var/lib/samba/private/sam.ldb"
  2. @Nockiro Nockiro revised this gist Mar 7, 2021. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions dhcp-dyndns.sh
    Original file line number Diff line number Diff line change
    @@ -51,7 +51,7 @@ _KERBEROS () {
    klist -c "${KRB5CCNAME}" -s
    if [ "$?" != "0" ]; then
    logger "${test} [dyndns] : Getting new ticket, old one has expired"
    if [[ "$OSTYPE" == "freebsd"* ]]; then
    if [[ "$OSTYPE" == "FreeBSD" ]]; then
    kinit --no-forwardable -k -t /etc/dhcpduser.keytab "${SETPRINCIPAL}"
    else
    kinit -F -k -t /etc/dhcpduser.keytab "${SETPRINCIPAL}"
    @@ -100,12 +100,12 @@ BINDIR=$(samba -b | grep 'BINDIR' | grep -v 'SBINDIR' | awk '{print $NF}')
    WBINFO="$BINDIR/wbinfo"

    LDBCOMMAND="ldbsearch"
    if [[ "$OSTYPE" == "freebsd"* ]]; then
    if [[ "$OSTYPE" == "FreeBSD" ]]; then
    LDBCOMMAND="samba-ldbsearch"
    fi

    LDBCOMMANDMOD="ldbmodify"
    if [[ "$OSTYPE" == "freebsd" ]]; then
    if [[ "$OSTYPE" == "FreeBSD" ]]; then
    LDBCOMMANDMOD="samba-ldbmodify"
    fi

  3. @Nockiro Nockiro revised this gist Mar 7, 2021. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion dhcp-dyndns.sh
    Original file line number Diff line number Diff line change
    @@ -4,8 +4,10 @@

    # This script is for secure DDNS updates on Samba,
    # it can also add the 'macAddress' to the Computers object.
    #
    # Modifications: Updates for faster local access to databases and compatibility with freebsds samba
    #
    # Version: 0.9.0
    # Version: 0.9.2
    #
    # Copyright (C) Rowland Penny 2020
    # Extended by Robin Freund 2020-2021
  4. @Nockiro Nockiro revised this gist Mar 7, 2021. 1 changed file with 20 additions and 2 deletions.
    22 changes: 20 additions & 2 deletions dhcp-dyndns.sh
    Original file line number Diff line number Diff line change
    @@ -49,7 +49,11 @@ _KERBEROS () {
    klist -c "${KRB5CCNAME}" -s
    if [ "$?" != "0" ]; then
    logger "${test} [dyndns] : Getting new ticket, old one has expired"
    kinit -F -k -t /etc/dhcpduser.keytab "${SETPRINCIPAL}"
    if [[ "$OSTYPE" == "freebsd"* ]]; then
    kinit --no-forwardable -k -t /etc/dhcpduser.keytab "${SETPRINCIPAL}"
    else
    kinit -F -k -t /etc/dhcpduser.keytab "${SETPRINCIPAL}"
    fi
    if [ "$?" != "0" ]; then
    logger "${test} [dyndns] : dhcpd kinit for dynamic DNS failed"
    exit 1;
    @@ -93,6 +97,16 @@ rev_zone_info () {
    BINDIR=$(samba -b | grep 'BINDIR' | grep -v 'SBINDIR' | awk '{print $NF}')
    WBINFO="$BINDIR/wbinfo"

    LDBCOMMAND="ldbsearch"
    if [[ "$OSTYPE" == "freebsd"* ]]; then
    LDBCOMMAND="samba-ldbsearch"
    fi

    LDBCOMMANDMOD="ldbmodify"
    if [[ "$OSTYPE" == "freebsd" ]]; then
    LDBCOMMANDMOD="samba-ldbmodify"
    fi

    # DHCP Server hostname
    Server=$(hostname -s)

    @@ -108,7 +122,11 @@ fi
    REALM=$(echo ${domain^^})

    # Samba SAM file - can also be ldap://${SERVER}, but that could get very slow
    DATABASE="/var/lib/samba/private/sam.ldb"
    if [[ "$OSTYPE" == "FreeBSD" ]]; then
    DATABASE="/var/db/samba4/private/sam.ldb"
    else
    DATABASE="/var/lib/samba/private/sam.ldb"
    fi

    # krbcc ticket cache
    export KRB5CCNAME="/tmp/dhcp-dyndns.cc"
  5. @Nockiro Nockiro created this gist Mar 3, 2021.
    360 changes: 360 additions & 0 deletions dhcp-dyndns.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,360 @@
    #!/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.0
    #
    # Copyright (C) Rowland Penny 2020
    # Extended by Robin Freund 2020-2021
    #
    # 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 <http://www.gnu.org/licenses/>.

    ##########################################################################
    # #
    # You can optionally add the 'macAddress' to the Computers object. #
    # Add 'dhcpduser' to the 'Domain Admins' group if used #
    # Change the next line to 'yes' to make this happen #
    Add_macAddress='yes'
    # #
    ##########################################################################

    usage() {
    echo "USAGE:"
    echo " $(basename $0) add ip-address dhcid|mac-address hostname"
    echo " $(basename $0) delete ip-address dhcid|mac-address"
    }

    _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 "${test} [dyndns] : Running check for valid kerberos ticket"
    klist -c "${KRB5CCNAME}" -s
    if [ "$?" != "0" ]; then
    logger "${test} [dyndns] : Getting new ticket, old one has expired"
    kinit -F -k -t /etc/dhcpduser.keytab "${SETPRINCIPAL}"
    if [ "$?" != "0" ]; then
    logger "${test} [dyndns] : dhcpd kinit for dynamic DNS failed"
    exit 1;
    fi
    fi
    }

    rev_zone_info () {
    local RevZone="$1"
    local IP="$2"
    local rzoneip=$(echo "$RevZone" | sed 's/\.in-addr.arpa//')
    local rzonenum=$(echo "$rzoneip" | sed 's/\./ /g')
    local words=($rzonenum)
    local numwords="${#words[@]}"
    case "$numwords" in
    1) # single ip rev zone '192'
    ZoneIP=$(echo "${IP}" | awk -F '.' '{print $1}')
    RZIP=$(echo "${rzoneip}" | awk -F '.' '{print $3}')
    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 '178.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
    echo "$ZoneIP"
    echo "$RZIP"
    echo "$IP2add"

    }

    BINDIR=$(samba -b | grep 'BINDIR' | grep -v 'SBINDIR' | awk '{print $NF}')
    WBINFO="$BINDIR/wbinfo"

    # DHCP Server hostname
    Server=$(hostname -s)

    # DNS domain
    domain=$(hostname -d)
    if [ -z ${domain} ]; then
    logger "Cannot obtain domain name, is DNS set up correctly?"
    logger "Cannot continue... Exiting."
    exit 1
    fi

    # Samba realm
    REALM=$(echo ${domain^^})

    # Samba SAM file - can also be ldap://${SERVER}, but that could get very slow
    DATABASE="/var/lib/samba/private/sam.ldb"

    # krbcc ticket cache
    export KRB5CCNAME="/tmp/dhcp-dyndns.cc"

    # Kerberos principal
    SETPRINCIPAL="dhcpduser@${REALM}"
    # Kerberos keytab : /etc/dhcpduser.keytab
    # krbcc ticket cache : /tmp/dhcp-dyndns.cc
    TESTUSER="$($WBINFO -u | grep 'dhcpduser')"
    if [ -z "${TESTUSER}" ]; then
    logger "No AD dhcp user exists, need to create it first.. exiting."
    logger "you can do this by typing the following commands"
    logger "kinit Administrator@${REALM}"
    logger "samba-tool user create dhcpduser --random-password --description='Unprivileged user for DNS updates via ISC DHCP server'"
    logger "samba-tool user setexpiry dhcpduser --noexpiry"
    logger "samba-tool group addmembers DnsAdmins dhcpduser"
    exit 1
    fi

    # Check for Kerberos keytab
    if [ ! -f /etc/dhcpduser.keytab ]; then
    echo "Required keytab /etc/dhcpduser.keytab not found, it needs to be created."
    echo "Use the following commands as root"
    echo "samba-tool domain exportkeytab --principal=${SETPRINCIPAL} /etc/dhcpduser.keytab"
    echo "chown XXXX:XXXX /etc/dhcpduser.keytab"
    echo "Replace 'XXXX:XXXX' with the user & group that dhcpd runs as on your distro"
    echo "chmod 400 /etc/dhcpduser.keytab"
    exit 1
    fi

    # Variables supplied by dhcpd.conf
    action="$1"
    ip="$2"
    DHCID="$3"
    name="${4%%.*}"

    # Exit if no ip address or mac-address
    if [ -z "${ip}" ] || [ -z "${DHCID}" ]; 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 "Invalid hostname '${name}' ...Exiting"
    exit
    ;;
    # * ) : ;;
    esac

    # exit if $name starts with 'dhcp'
    # if you do not want computers without a hostname in AD
    # uncomment the following block of code.
    if [[ $name == dhcp* ]]; then
    logger "not updating DNS record in AD, invalid name"
    exit 0
    fi

    ## update ##
    case "${action}" in
    add)
    _KERBEROS

    # does host have an existing 'A' record ?
    A_REC=$(host -t A "${name}" | awk '{print $NF}')
    # check for dots
    if [[ $A_REC == *.* ]]; then
    if [[ ${ip} = $A_REC ]]; then
    logger "Existing A record of ${name} matches new IP, not updating."
    result1="0"
    result2="0"
    else
    samba-tool dns delete ${Server} ${domain} "${name}" A ${ip} -k yes
    result1="$?"
    logger "Deleted existing host A record for ${name}"

    samba-tool dns add ${Server} ${domain} "${name}" A ${ip} -k yes
    result2="$?"
    logger "Added new A record"

    fi
    else
    result1=0
    logger "No A record yet"

    samba-tool dns add ${Server} ${domain} "${name}" A ${ip} -k yes
    result2="$?"
    logger "Added new A record"
    fi

    # get ptr record for possible early exit
    PTR_REC=$(host -t PTR "${ip}" | awk '{print $NF}')
    if [[ $PTR_REC = "${name}".${domain}. ]]; then
    logger "Existing PTR record of ${ip} matches new hostname, not updating."
    result3='0'
    result4='0'
    else
    # get existing reverse zones (if any)
    ReverseZones=$(samba-tool dns zonelist ${Server} --reverse | grep 'pszZoneName' | awk '{print $NF}')

    if [ -z "$ReverseZones" ]; then
    echo "No reverse zone found, not updating."
    result3="0"
    result4="0"
    else
    for revzone in $ReverseZones
    do
    logger "Looking in zone ${revzone}.${domain}"
    rev_zone_info "$revzone" "${ip}"
    if [[ ${ip} = $ZoneIP* ]] && [ "$ZoneIP" = "$RZIP" ]; then
    if [[ $PTR_REC == *.${domain}. ]]; then
    samba-tool dns delete ${Server} ${revzone} ${IP2add} PTR "${name}".${domain} -k yes
    result3="$?"
    logger "Deleted existing host PTR record for {ip}"

    samba-tool dns add ${Server} ${revzone} ${IP2add} PTR "${name}".${domain} -k yes
    result4="$?"
    logger "Added new PTR record"
    else
    result3='0'
    logger "No PTR record for ${ip} yet - found $PTR_REC for ${domain}"

    samba-tool dns add ${Server} ${revzone} ${IP2add} PTR "${name}".${domain} -k yes
    result4="$?"
    logger "Added new PTR record"

    fi

    break
    else
    continue
    fi
    done
    fi
    fi
    ;;
    delete)
    _KERBEROS

    samba-tool dns delete ${Server} ${domain} "${name}" A ${ip} -k yes
    result1="$?"
    # get existing reverse zones (if any)
    ReverseZones=$(samba-tool dns zonelist ${Server} --reverse | grep 'pszZoneName' | awk '{print $NF}')
    if [ -z "$ReverseZones" ]; then
    logger "No reverse zone found, not updating"
    result2='0'
    else
    for revzone in $ReverseZones
    do
    rev_zone_info "$revzone" "${ip}"
    if [[ ${ip} = $ZoneIP* ]] && [ "$ZoneIP" = "$RZIP" ]; then
    host -t PTR ${ip} > /dev/null 2>&1
    if [ "$?" -eq 0 ]; then
    samba-tool dns delete ${Server} ${revzone} ${IP2add} PTR "${name}".${domain} -k yes
    result2="$?"
    else
    result2='0'
    fi
    break
    else
    continue
    fi
    done
    fi
    result3='0'
    result4='0'
    ;;
    *)
    logger "Invalid action specified"
    exit 103
    ;;
    esac

    result="${result1}:${result2}:${result3}:${result4}"

    if [ "${result}" != "0:0:0:0" ]; then
    logger "DHCP-DNS Update failed: ${result}"
    exit 1
    else
    logger "DHCP-DNS Update succeeded"
    fi

    if [ "$Add_macAddress" != 'no' ]; then
    logger "Searching for $name in AD.."
    Computer_Object=$(ldbsearch -k yes -H "$DATABASE" "(&(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 -k yes -H "$DATABASE" "(&(objectclass=computer)(cn=$name))" | grep -v '#' | grep -v 'ref:')
    if [ -z "$Computer_Object" ]; then
    logger "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 -k yes -H "$DATABASE" 2&>1 | logger &
    ret="$?"
    if [ "$ret" -ne 0 ]; then
    logger "Error modifying Computer objectclass $name in AD."
    exit "${ret}"
    fi
    sleep 2
    echo "$attrldif" | ldbmodify -k yes -H "$DATABASE" 2&>1 | logger &
    ret="$?"
    if [ "$ret" -ne 0 ]; then
    logger "Error modifying Computer attribute $name in AD."
    exit "${ret}"
    fi
    unset objldif
    unset attrldif
    logger "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 -k yes -H ldap://"$Server" 2&>1 | logger &
    ret="$?"
    if [ "$ret" -ne 0 ]; then
    logger "Error modifying Computer attribute $name in AD."
    exit "${ret}"
    fi
    unset attrldif
    logger "Successfully modified Computer $name in AD"
    fi
    fi
    exit 0