Samba4 AD DC + DHCPD + BIND (Dynamic DNS)

Samba 4 已經釋出很長一段時間了 因此趁著升級電腦與 centos 6 順便將 Samba 3 AD DC + dhcpd + bind 更新為 Samba 4 + bind 9 dlz 模組 + dhcpd

DC DQDN名稱 : samba4dc

DC IP : 192.168.0.1/24

  1. 首先到 https://portal.enterprisesamba.com/users/sign_up 註冊. 方便使用 yum 升級 samba , 如果使用 source 安裝就不需申請. 登入後把給 centos6 用的 sernet-samba-4.1.repo 檔下載回來並修改檔案中 USERNAME:ACCESSKEY 的部分.
  2. 升級前先備份 /etc/samba, /var/lib/samba, /etc/dhcp, /etc/named.conf, /var/named, /var/cache/samba 等目錄
  3. 安裝 samba 4
    # yum install sernet-samba -y
  4. 升級 Samba AD 網域
    移動設定檔

    mv /etc/smb.conf /etc/smb.PDC.conf

    將舊資料庫等檔案移到其他位置

    # mv /var/lib/samba/ /var/lib/samba.PDC/
    # mv /var/cache/samba/ /var/cache/samba.PDC/

    新增資料夾. 並將升級時會用到的資料庫檔案複製到該目錄

    # mkdir /var/lib/samba.PDC/dbdir/
    # cp -p /var/lib/samba.PDC/private/secrets.tdb /var/lib/samba.PDC/dbdir/
    # cp -p /var/lib/samba.PDC/private/schannel_store.tdb /var/lib/samba.PDC/dbdir/
    # cp -p /var/lib/samba.PDC/private/passdb.tdb /var/lib/samba.PDC/dbdir/
    # cp -p /var/cache/samba.PDC/gencache_notrans.tdb /var/lib/samba.PDC/dbdir/
    # cp -p /var/cache/samba.PDC/group_mapping.tdb /var/lib/samba.PDC/dbdir/
    # cp -p /var/cache/samba.PDC/account_policy.tdb /var/lib/samba.PDC/dbdir/

    升級資料庫以及使用 bind_dlz 模組

    # samba-tool domain classicupgrade --dbdir=/var/lib/samba.PDC/dbdir/ --use-xattrs=yes --realm=home.com --dns-backend=BIND9_DLZ /etc/smb.PDC.conf --option="interfaces=lo eth0" --option="bind interfaces only=yes"
    PS: --option="interfaces=lo eth0" --option="bind interfaces only=yes" 這是限定 samba 只作用在網卡 loopback 及 eth0
  5. 調整產生的 samba 設定即啟動檔
    /etc/samba/smb.conf

    [global]
            workgroup = HOME
            realm = home.com
            netbios name = SAMBA4DC
            interfaces = lo, eth0
            bind interfaces only = Yes
            server role = active directory domain controller
            server services = s3fs, rpc, nbt, wrepl, ldap, cldap, kdc, drepl, winbind, ntp_signd, kcc, dnsupdate
            idmap_ldb:use rfc2307 = yes
            log level = 2
            log file = /var/log/samba/log.%m
            max log size = 50
            wins support = yes
            syslog = 0
            username map = /etc/samba/smbusers
            domain master = yes
            domain logons = yes
            local master = yes
            preferred master = yes
    
    [netlogon]
            path = /var/lib/samba/sysvol/home.com/scripts
            read only = No
    
    [sysvol]
            path = /var/lib/samba/sysvol
            read only = No
    
    [homes]
            comment = Home Directories
            browseable = no
            read only = No
            valid users = %S
            create mask = 0700
            directory mask = 0700
    
    [nas]
            comment = storage
            path = /home/data
            public = yes
            writable = yes
            printable = no
            write list = +adm

    因為是作為 dc , 只要開啟 sernet-samba-ad 服務. 其餘 sernet-samba-nmbd, sernet-samba-smbd, sernet-samba-winbindd 不須啟動.

    # chkconfig --level 2345 sernet-samba-ad on
  6. 調整 bind9 & kerberos 設定
    /etc/named.conf

    options {
            listen-on port 53 { 127.0.0.1; };
            listen-on-v6 port 53 { ::1; };
            directory       "/var/named";
            dump-file       "/var/named/data/cache_dump.db";
            statistics-file "/var/named/data/named_stats.txt";
            memstatistics-file "/var/named/data/named_mem_stats.txt";
    
            forwarders {
                    168.95.1.1;
                    };
            forward first;
            listen-on {
                    home;
            };
            allow-recursion {home;};
            allow-update { home; };
            allow-query { home; };
            allow-transfer { home;};
            listen-on-v6 { none; };
            tkey-gssapi-keytab "/var/lib/samba/private/dns.keytab"; #加入這行
            auth-nxdomain yes;
            empty-zones-enable no;
            version none;
            hostname none;
            server-id none;
    };
    
    logging {
            channel Named_log {
                file "data/named.run";# versions 5 size 5m;
                severity dynamic;
                print-severity  yes;
                print-time yes; };
            category default {Named_log; };
            category xfer-out {Named_log; };
            category queries {Named_log; };
    };
    
    acl "home" { 192.168.0.0/24; 127.0.0.1;};
    
    controls {
            inet 127.0.0.1 allow { localhost; } keys { rndc-key; };
    };
    
    zone "." IN {
            type hint;
            file "named.ca";
    };
    
    include "/etc/named.rfc1912.zones";
    include "/etc/named.root.key";
    include "/etc/rndc.key";
    include "/var/lib/samba/private/named.conf";   #加入這行

    /var/lib/samba/private/named.conf

    lz "AD DNS Zone" {
        # For BIND 9.8.0
        database "dlopen /usr/lib64/samba/bind9/dlz_bind9.so"; #選擇對應的 bind 版本
    
        # For BIND 9.9.0
        # database "dlopen /usr/lib64/samba/bind9/dlz_bind9_9.so";
    };

    修改 keytab 檔案權限讓 bind 可以用其來動態更新 zone

    # chgrp named /var/lib/samba/private
    # chgrp named /var/lib/samba/private/dns.keytab
    # chmod g+r /var/lib/samba/private/dns.keytab

    連結 kerberos 設定檔到 /etc 下

    # ln -sf /var/lib/samba/private/krb5.conf /etc/krb5.conf

    重啟 named

    # service named restart

    增加反解析區域

    # samba-tool dns zonecreate samba4dc.home.com 0.168.192.in-addr.arpa

    將 DC 的 PTR 加入反解區域

    # samba-tool dns add samba4dc.home.com 0.168.192.in-addr.arpa 1 PTR samba4dc.home.com

    如果有設定 ntpd (選擇性) 請修改 socket 的位址讓 samba 可以讀取

    driftfile /var/lib/ntp/drift
    logfile         /var/log/ntp
    ntpsigndsocket  /var/lib/samba/ntp_signd
    
    restrict default kod nomodify notrap nopeer mssntp noquery
    restrict -6 default kod nomodify notrap nopeer noquery
    
    restrict 127.0.0.1
    restrict -6 ::1
    
    restrict 192.168.0.0 mask 255.255.255.0 nomodify notrap
    
    server 0.centos.pool.ntp.org iburst
    server 1.centos.pool.ntp.org iburst
    server 2.centos.pool.ntp.org iburst
    server 3.centos.pool.ntp.org iburst
    
    includefile /etc/ntp/crypto/pw
    
    keys /etc/ntp/keys

    修改權限及重啟 ntpd

    # chown root:ntp /var/lib/samba/ntp_signd
    # chmod 750 /var/lib/samba/ntp_signd
    # service ntpd restart

    重啟 samba

    # service smbd stop
    # service nmbd stop
    # service sernet-samba-ad start
  7. DHCPD 設定
    在 AD 裡新增 dhcp 這個使用者並給予權限讓它可以動態更新 DNS(也可以使用 Windows 遠端伺服器管理工具 RSAT 來新增)

    # samba-tool user create dhcp --description="Unprivileged user for DNS updates via DHCP server"
    # samba-tool group addmembers DnsAdmins dhcp

    將該使用者憑證匯出成 keytab 檔

    # cd /etc/dhcp
    # samba-tool domain exportkeytab --principal=dhcp@HOME.COM dhcpd.keytab
    # chown root:dhcpd /etc/dhcp/dhcpd.keytab
    # chmod 440 /etc/dhcp/dhcpd.keytab
    # chgrp dhcpd /etc/dhcp

    新增以下兩個執行檔
    /usr/local/sbin/dhcp-dyndns.sh

    #!/bin/bash
    $(dirname $0)/dns-krbnsupdate.sh $@ 2>&1 | logger &

    /usr/local/sbin/dns-krbnsupdate.sh

    #!/bin/bash
    
    # This script is for secure DDNS updates using GSS/TSIG
    # Version: 0.1
    
    ## CONFIGURATION ##
    # Kerberos realm
    realm="HOME.COM"
    # Kerberos principal
    principal="dhcp@$realm"
    # Kerberos keytab
    keytab="/etc/dhcp/dhcpd.keytab"
    # Kerberos credentials cache
    krb5cc="/tmp/krb5cc_0"
    # Use MIT kerberos args instead of heimdal.
    KRB5MIT="YES"
    
    # Domain appended to hostname
    domain="home.com"
    # Space separated list of DNS servers for sending updates to
    NSRVS="cchisheng.home.com"
    # Default DNS resource records TTL
    RRTTL="3600"
    # Do not use TXT RRs (rfc4701)
    NOTXTRRS="YES"
    
    # Additional nsupdate flags (-g already applied), e.g. "-d" for debug
    #NSUPDFLAGS="-d"
    # Run in the foreground (for manual run only!!!), it's better to use "-d" as script's first argument
    #DEBUG="YES"
    
    ######################################################
    
    ## VARIABLES ##
    [ "$1" = "-d" ] && DEBUG="YES" && shift
    action=$1
    ip=$2
    DHCID=$3
    name=${4%%.*}
    [ -n "$5" ] && RRTTL="$5"
    
    _usage() {
    echo "Usage:"
    echo " `basename $0` [-d] add ip-address dhcid|mac-address hostname [dns-ttl]"
    echo " `basename $0` [-d] delete ip-address dhcid|mac-address"
    }
    
    _kerberos() {
    export KRB5_KTNAME="$keytab"
    export KRB5CCNAME="$krb5cc"
    
    if [ "$KRB5MIT" = "YES" ]; then
     KLISTARG="-s"
    else
     KLISTARG="-t"
    fi
    
    klist $KLISTARG || kinit -k -t "$keytab" -c "$krb5cc" "$principal" || { echo "DDNS: kinit failed"; exit 1; }
    }
    
    _main() {
    umask 77
    
    if [ -z "$ip" ] || [ -z "$DHCID" ]; then
     _usage
     exit 1
    fi
    
    
    ## NSUPDATE ##
    case "$action" in
     add)
     RRPTR="$name.$domain"
     if [ "$NOTXTRRS" != "YES" ]; then
     NOTXTRRS=""
     RRAOLD=`host $RRPTR | awk '/has address/ {print $4}'`
     if [ -n "$RRAOLD" ]; then
     RRTXTOLD=`host -t txt "$RRPTR" | sed -n '/descriptive text/s/^.*[[:space:]]descriptive text[[:space:]]*"\(.*\)"$/\1/p'`
     [ -z "$RRTXTOLD" ] && echo "DDNS: adding records for $ip ($RRPTR) FAILED: has A record but no DHCID, not mine" && exit 1
    
     RRTXT=`echo "$DHCID$RRPTR" | sha256sum`
     RRTXT="000101${RRTXT%% *}"
     [ "$RRTXT" != "$RRTXTOLD" ] && echo "DDNS: adding records for $ip ($RRPTR) FAILED: has A record but DHCID is wrong" && exit 1
     else
     RRTXT=`echo "$DHCID$RRPTR" | sha256sum`
     RRTXT="000101${RRTXT%% *}"
     fi
     else
     NOTXTRRS=";"
     fi
    
     RRPTRNAME=`echo $ip | awk -F '.' '{print $4"."$3"."$2"."$1".in-addr.arpa"}'`
    
     _kerberos
    
     for NSRV in $NSRVS; do
     nsupdate -g $NSUPDFLAGS << UPDATE
    server $NSRV
    realm $realm
    update delete $RRPTR. $RRTTL A
    ${NOTXTRRS}update delete $RRPTR. $RRTTL TXT
    ${NOTXTRRS}update add $RRPTR. $RRTTL TXT $RRTXT
    update add $RRPTR. $RRTTL A $ip
    send
    update delete $RRPTRNAME. $RRTTL PTR
    update add $RRPTRNAME. $RRTTL PTR $name.$domain.
    send
    UPDATE
      result=$?
     [ "$result" -eq "0" ] && echo "DDNS: adding records for $ip ($RRPTR) succeeded" && exit 0
     done
    
     [ "$result" != "0" ] && echo "DDNS: adding records for $ip ($RRPTR) FAILED: nsupdate status $result" && exit "$result"
     ;;
     delete)
     RRPTR=`host $ip | awk '/domain name pointer/ { sub(/\.$/, "", $5); print $5}'`
     if [ "$NOTXTRRS" != "YES" ]; then
     NOTXTRRS=""
     if [ -n "$RRPTR" ]; then
     RRTXTOLD=`host -t txt "$RRPTR" | sed -n '/descriptive text/s/^.*[[:space:]]descriptive text[[:space:]]*"\(.*\)"$/\1/p'`
     [ -z "$RRTXTOLD" ] && echo "DDNS: removing records for $ip ($RRPTR) FAILED: has A record but no DHCID, not mine" && exit 1
    
     RRTXT=`echo "$DHCID$RRPTR" | sha256sum`
     RRTXT="000101${RRTXT%% *}"
     [ "$RRTXT" != "$RRTXTOLD" ] && echo "DDNS: removing records for $ip ($RRPTR) FAILED: has A record but DHCID is wrong" && exit 1
     else
     echo "DDNS: removing records for $ip FAILED: has no PTR, can not determine A record" && exit 1
     fi
     else
     NOTXTRRS=";"
     fi
    
     RRPTRNAME=`echo $ip | awk -F '.' '{print $4"."$3"."$2"."$1".in-addr.arpa"}'`
    
     _kerberos
    
     for NSRV in $NSRVS; do
     nsupdate -g $NSUPDFLAGS << UPDATE
    server $NSRV
    realm $realm
    update delete $RRPTR. $RRTTL A
    ${NOTXTRRS}update delete $RRPTR. $RRTTL TXT
    send
    update delete $RRPTRNAME. $RRTTL PTR
    send
    UPDATE
     result=$?
     [ "$result" -eq "0" ] && echo "DDNS: removing records for $ip ($RRPTR) succeeded" && exit 0
     done
    
     [ "$result" != "0" ] && echo "DDNS: removing records for $ip ($RRPTR) FAILED: nsupdate status $result" && exit "$result"
     ;;
     *)
     _usage && exit 1
     ;;
    esac
    }
    
    if [ "$DEBUG" = "YES" ]; then
     _main
    else
     :
     _main | logger -s -t dhcpd &
    fi

    修改 /etc/dhcp/dhcpd.conf

    subnet 192.168.0.0 netmask 255.255.255.0 {
     authoritative;
     option netbios-name-servers 192.168.0.1;
     option domain-name-servers 192.168.0.1;
     option domain-name "home.com";
     option netbios-node-type 8;
     option routers 192.168.1.1;
     option subnet-mask 255.255.255.0;
     default-lease-time 259200;
     range 192.168.0.10 192.168.1.250;
     on commit {
     set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
     set ClientName = pick-first-value(option host-name, host-decl-name);
     set ClientDHCID = binary-to-ascii(16, 8, ":", hardware);
     execute("/usr/local/sbin/dhcp-dyndns.sh", "add", ClientIP, ClientDHCID, ClientName);
     }
     on release {
     set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
     set ClientName = pick-first-value(option host-name, host-decl-name);
     set ClientDHCID = binary-to-ascii(16, 8, ":", hardware);
     execute("/usr/local/sbin/dhcp-dyndns.sh", "delete", ClientIP, ClientDHCID);
     }
     on expiry {
     set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
     set ClientName = pick-first-value(option host-name, host-decl-name);
     set ClientDHCID = binary-to-ascii(16, 8, ":", hardware);
     execute("/usr/local/sbin/dhcp-dyndns.sh", "delete", ClientIP, ClientDHCID);
     }
    }

    改變檔案權限

    # chmod 755 /usr/local/sbin/dhcp-dyndns.sh
    # chmod 755 /usr/local/sbin/dns-krbnsupdate.sh

    重啟 dhcpd

    # service dhcpd restart
  8. 測試
    # samba_dnsupdate --verbose --all-names

    若上述指令沒有錯誤表示 bind9 設定正確. 若有錯誤請檢查檔案及目錄權限, 讓 bind9 可以讀取  dns.keytab
    當 dhcp client 端開機或者在 Windows 命令提示視窗執行 ipconfig /renew 時, server上的 syslog 會有以下紀錄, 表示動態更新成功

    Dec  9 00:00:15 samba4dc dhcpd: DDNS: adding records for 192.168.0.186 (win7.home.com) succeeded
    Dec  9 00:00:15 samba4dc logger: dhcpd: DDNS: adding records for 192.168.0.186 (win7.home.com) succeeded
  9. 問題
    I.Windows client本身會執行 secure dns update, 因此 named 紀錄裡會有更新失敗的錯誤. 由於我們是透過dhcpd更新dns, 因此該錯誤可以忽略.
    II.Samba4目前對於讓 dc 及file server 在同台伺服器上尚未完整支援, 因此 client 端在網路芳鄰內找不到 server, 需要手動輸入 \\server 連結伺服器. 如果在登入指令檔或者 RSAT 的使用者管理裡指定連線磁碟機就可以解決該問題
    III. RSAT 的 DNS 管理員可以新增刪除紀錄但有時會有錯誤訊息但不影響
  10. 參考文獻
    https://wiki.samba.org/index.php/User_Documentation
    http://blog.michael.kuron-germany.de/2011/02/isc-dhcpd-dynamic-dns-updates-against-secure-microsoft-dns/
    https://wiki.archlinux.org/index.php/Samba_4_Active_Directory_Domain_Controller