diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d72491..caac547 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,47 @@ # NetSUS Changelog +## 4.2.1 + +* Added High Sierra support for SUS +* Updated reposado to latest version +* Fixed an issue where certain Sierra build would NetBoot extremely slowly + +## 4.2 + +* Added Sierra support for SUS +* Updated reposado to latest version +* Added validation for SUS Base URL and Branch name(s) with live feedback +* Added (missing) option for SUS sync at 9:00 AM +* Improved detection of the last SUS sync date and time +* Added proxy configuration to SUS +* Added validation for NetBoot Image Name, Subnet and Netmask with live feedback +* Added checks for NetBoot supporting services +* Provisioned for NFS support for NetBoot Images +* Updated service controls for TFTP on RHEL/CentOS +* Added validation for Hostname, IP Address, Netmask, Gateway and DNS Servers with live feedback +* Added functionality to dynamically determine primary network interface, to allow for variations +* Updated network configuration to persistently configure static DNS on Ubuntu +* Updated service controls for SSH and Firewall +* Updated timezone configuration +* Added validation for Network Time Server with live feedback +* Added functionality to create CSR (and new Private Key) in webadmin GUI +* Added field descriptions for certificates +* Added functionality to view logs in the webadmin GUI +* Added Web UI for expanding the primary volume, when the underlying VMDK is expanded +* Added functionality to enable/disable AFP service +* Added functionality to enable/disable SMB service +* Updated about page to reflect OS and installed packages +* Updated Jamf Nation links +* Removed support for Ubuntu 10.04 and 12.04 (both are EOL) +* Added support for Ubuntu 16.04 +* Removed hard-coded OS checks, replaced with detection of binaries or configuration files +* Improved detection/installation of supporting software +* Installer now updates existing files in-place, rather than overwriting with templates +* Updated mechanisms used for Ubuntu service controls to ensure services are correctly enabled / disabled +* Firewall rule configuration removed from adminHelper.sh, all firewall rules are pre-configured during installation +* Added 'enablegui' option to adminHelper.sh to easily re-enable webadmin GUI +* Fixed an issue where the LDAP Proxy prompts for a password during installation + ## 4.1 * New and improved User Interface and minor changes to the User Experience diff --git a/CreateNetSUSInstaller.sh b/CreateNetSUSInstaller.sh index 6b14d40..8d7affd 100755 --- a/CreateNetSUSInstaller.sh +++ b/CreateNetSUSInstaller.sh @@ -15,41 +15,66 @@ timeEcho "Building NetSUSLP Installer..." rm -f NetSUSInstaller.run 2>&1 > /dev/null rm -Rf temp 2>&1 > /dev/null -mkdir temp -cp -R base temp -cp -R NetBoot temp -cp -R SUS temp -cp -R webadmin temp -cp -R LDAPProxy temp -cp -R includes/* temp/base/ -cp -R includes/* temp/NetBoot/ -cp -R includes/* temp/SUS/ -cp -R includes/* temp/webadmin/ -cp -R includes/* temp/LDAPProxy/ -if [ -x /usr/bin/xattr ]; then find temp -exec xattr -c {} \; ;fi # Remove OS X extended attributes +#mkdir temp +#cp -R base temp +#cp -R NetBoot temp +#cp -R SUS temp +#cp -R webadmin temp +#cp -R LDAPProxy temp +#cp -R includes/* temp/base/ +#cp -R includes/* temp/NetBoot/ +#cp -R includes/* temp/SUS/ +#cp -R includes/* temp/webadmin/ +#cp -R includes/* temp/LDAPProxy/ +mkdir -p temp/installer/checks +mkdir -p temp/installer/resources +mkdir -p temp/installer/utils +cp -R base/NetSUSInstaller.sh temp/installer/install.sh +cp -R base/test64bitRequirements.sh temp/installer/checks/test64bitRequirements.sh +cp -R base/testOSRequirements.sh temp/installer/checks/testOSRequirements.sh +cp -R base/testUbuntuBinRequirements.sh temp/installer/checks/testBinRequirements.sh +cp -R includes/logger.sh temp/installer/utils/logger.sh +cp -R LDAPProxy/etc/ldap/* temp/installer/resources +cp -R LDAPProxy/LDAPProxyInstall.sh temp/installer/install-proxy.sh +cp -R NetBoot/netbootInstall.sh temp/installer/install-netboot.sh +cp -R NetBoot/usr/local/sbin temp/installer/resources/dhcp +cp -R NetBoot/var/appliance/conf/dhcpd.conf temp/installer/resources/dhcpd.conf +cp -R NetBoot/var/appliance/configurefornetboot temp/installer/resources/configurefornetboot +cp -R NetBoot/var/appliance/libdb4-4.8.30-21.fc26.x86_64.rpm temp/installer/resources/libdb4-4.8.30-21.fc26.x86_64.rpm +cp -R NetBoot/var/appliance/netatalk-2.2.0-2.el6.x86_64.rpm temp/installer/resources/netatalk-2.2.0-2.el6.x86_64.rpm +cp -R NetBoot/var/appliance/netatalk-2.2.3-9.fc20.x86_64.rpm temp/installer/resources/netatalk-2.2.3-9.fc20.x86_64.rpm +cp -R SUS/susInstall.sh temp/installer/install-sus.sh +cp -R SUS/var/appliance/sus_sync.py temp/installer/resources/sus_sync.py +cp -R SUS/var/lib/reposado temp/installer/resources/reposado +cp -R webadmin/webadminInstall.sh temp/installer/install-webadmin.sh +cp -R webadmin/var/appliance/dialog.sh temp/installer/resources/dialog.sh +cp -R webadmin/var/www temp/installer/resources/html +rm -f temp/installer/resources/html/webadmin/scripts/netbootname.py +if [ -x "/usr/bin/xattr" ]; then find temp -exec xattr -c {} \; ;fi # Remove OS X extended attributes find temp -name .DS_Store -delete # Clean out .DS_Store files find temp -name .svn | xargs rm -Rf # Clean out SVN garbage # Generate NetBoot App sub-installer -timeEcho "Creating NetBoot sub-installer..." -bash makeself/makeself.sh temp/NetBoot/ temp/base/netbootInstall.run "NetBoot Installer" "bash netbootInstall.sh" > /dev/null +#timeEcho "Creating NetBoot sub-installer..." +#bash makeself/makeself.sh temp/NetBoot/ temp/base/netbootInstall.run "NetBoot Installer" "bash netbootInstall.sh" > /dev/null # Generate SUS sub-installer -timeEcho "Creating SUS sub-installer..." -bash makeself/makeself.sh temp/SUS/ temp/base/susInstall.run "SUS Installer" "bash susInstall.sh" > /dev/null +#timeEcho "Creating SUS sub-installer..." +#bash makeself/makeself.sh temp/SUS/ temp/base/susInstall.run "SUS Installer" "bash susInstall.sh" > /dev/null # Generate webadmin sub-installer -timeEcho "Creating webadmin sub-installer..." -bash makeself/makeself.sh temp/webadmin/ temp/base/webadminInstall.run "WebAdmin Installer" "bash webadminInstall.sh" > /dev/null +#timeEcho "Creating webadmin sub-installer..." +#bash makeself/makeself.sh temp/webadmin/ temp/base/webadminInstall.run "WebAdmin Installer" "bash webadminInstall.sh" > /dev/null # Generate LDAP Proxy sub-installer -timeEcho "Creating LDAP Proxy sub-installer..." -bash makeself/makeself.sh temp/LDAPProxy/ temp/base/LDAPProxyInstall.run "LDAP Proxy Installer" "bash LDAPProxyInstall.sh" > /dev/null +#timeEcho "Creating LDAP Proxy sub-installer..." +#bash makeself/makeself.sh temp/LDAPProxy/ temp/base/LDAPProxyInstall.run "LDAP Proxy Installer" "bash LDAPProxyInstall.sh" > /dev/null # Generate final installer timeEcho "Creating final installer..." -bash makeself/makeself.sh temp/base/ NetSUSLPInstaller.run "NetSUSLP Installer" "bash NetSUSInstaller.sh" +#bash makeself/makeself.sh temp/base/ NetSUSLPInstaller.run "NetSUSLP Installer" "bash NetSUSInstaller.sh" +bash makeself/makeself.sh temp/installer/ NetSUSLPInstaller.run "NetSUSLP Installer" "bash install.sh" timeEcho "Cleaning up..." #cp temp/*/*.run . # Uncomment this if you want to test the sub-installers outside of the main installer diff --git a/LDAPProxy/LDAPProxyInstall.sh b/LDAPProxy/LDAPProxyInstall.sh index a547c0f..c8a23e5 100644 --- a/LDAPProxy/LDAPProxyInstall.sh +++ b/LDAPProxy/LDAPProxyInstall.sh @@ -1,13 +1,28 @@ #!/bin/bash # This script controls the flow of the LDAP Proxy installation -pathToScript=$0 -detectedOS=$1 -# Logger -source logger.sh +log "Starting LDAP Proxy Installation" -logEvent "Starting LDAP Proxy Installation" -if [[ $detectedOS == 'Ubuntu' ]]; then +apt_install() { + if [[ $(apt-cache -n search ^${1}$ | awk '{print $1}' | grep ^${1}$) == "$1" ]] && [[ $(dpkg -s $1 2>&- | awk '/Status: / {print $NF}') != "installed" ]]; then + apt-get -qq -y install $1 >> $logFile 2>&1 + if [[ $? -ne 0 ]]; then + exit 1 + fi + fi +} + +yum_install() { + if yum -q list $1 &>- && [[ $(rpm -qa $1) == "" ]] ; then + yum install $1 -y -q >> $logFile 2>&1 + if [[ $? -ne 0 ]]; then + exit 1 + fi + fi +} + +# Install required software +if [[ $(which apt-get 2>&-) != "" ]]; then export DEBIAN_FRONTEND=noninteractive echo -e " \ slapd slapd/internal/generated_adminpw password netsuslp @@ -15,46 +30,58 @@ slapd slapd/password2 password netsuslp slapd slapd/internal/adminpw password netsuslp slapd slapd/password1 password netsuslp " | sudo debconf-set-selections - apt-get -qq -y install slapd >> $logFile - export DEBIAN_FRONTEND= + apt_install slapd + unset DEBIAN_FRONTEND +elif [[ $(which yum 2>&-) != "" ]]; then + yum_install openldap-servers + yum_install expect fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - if ! rpm -qa "*openldap-servers*" | grep -q "openldap-servers" ; then - yum install openldap-servers -y -q >> $logFile +# Prepare the firewall in case it is enabled later +if [[ $(which ufw 2>&-) != "" ]]; then + # LDAP + ufw allow 389/tcp >> $logFile +elif [[ $(which firewall-cmd 2>&-) != "" ]]; then + # LDAP + firewall-cmd --zone=public --add-port=389/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=389/tcp --permanent >> $logFile 2>&1 +else + # LDAP + if iptables -L | grep DROP | grep -q 'tcp dpt:ldap' ; then + iptables -D INPUT -p tcp --dport 389 -j DROP fi + if ! iptables -L | grep ACCEPT | grep -q 'tcp dpt:ldap' ; then + iptables -I INPUT -p tcp --dport 389 -j ACCEPT + fi + service iptables save >> $logFile 2>&1 fi -if [[ $detectedOS == 'Ubuntu' ]]; then - rm -rf /etc/ldap/slapd.d/ >> $logFile - cp -R ./etc/* /etc/ - sed -i "s/SLAPD_SERVICES=\"ldap:\/\/\/ ldapi:\/\/\/\"/SLAPD_SERVICES=\"ldap:\/\/\/ ldapi:\/\/\/ ldaps:\/\/\/\"/g" /etc/default/slapd -fi - -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - rm -rf /etc/openldap/slapd.d/ >> $logFile - cp -R ./etc/ldap/slapdyum.conf /etc/openldap/slapd.conf - sed -i "s/SLAPD_URLS=\"ldapi:\/\/\/ ldap:\/\/\/\"/SLAPD_URLS=\"ldapi:\/\/\/ ldap:\/\/\/\ ldaps:\/\/\/\"/g" /etc/sysconfig/slapd -fi - -cp -R ./var/* /var/ - -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - rm /var/appliance/conf/slapd.conf - mv /var/appliance/conf/slapdyum.conf /var/appliance/conf/slapd.conf -else - rm /var/appliance/conf/slapdyum.conf +# Create appliance configuration directory +if [ ! -d "/var/appliance/conf" ]; then + mkdir /var/appliance/conf fi - -if [[ $detectedOS == 'Ubuntu' ]]; then +# Configure slapd +if [ -d "/etc/ldap" ]; then + rm -rf /etc/ldap/slapd.d/ >> $logFile + cp ./resources/slapd.conf /etc/ldap/slapd.conf >> $logFile + cp ./resources/slapd.conf /var/appliance/conf/slapd.conf >> $logFile + sed -i '/\/var\/appliance\/conf\//d' /etc/apparmor.d/usr.sbin.slapd + sed -i -e '//{:a;n;/^$/!ba;i\ \/var\/appliance\/conf\/ r,\n \/var\/appliance\/conf\/* r,' -e '}' /etc/apparmor.d/usr.sbin.slapd + sed -i "s/SLAPD_SERVICES=\"ldap:\/\/\/ ldapi:\/\/\/\"/SLAPD_SERVICES=\"ldap:\/\/\/ ldapi:\/\/\/ ldaps:\/\/\/\"/g" /etc/default/slapd cp /etc/ssl/certs/ssl-cert-snakeoil.pem /var/appliance/conf/appliance.chain.pem cp /etc/ssl/certs/ssl-cert-snakeoil.pem /var/appliance/conf/appliance.certificate.pem cp /etc/ssl/private/ssl-cert-snakeoil.key /var/appliance/conf/appliance.private.key chown openldap /var/appliance/conf/appliance.private.key fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - cp /etc/pki/tls/certs/server-chain.crt /var/appliance/conf/appliance.chain.pem +if [ -d "/etc/openldap" ]; then + rm -rf /etc/openldap/slapd.d/ >> $logFile + cp ./resources/slapdyum.conf /etc/openldap/slapd.conf >> $logFile + cp ./resources/slapdyum.conf /var/appliance/conf/slapd.conf >> $logFile + if [ -f "/etc/sysconfig/slapd" ]; then + sed -i "s/SLAPD_URLS=\"ldapi:\/\/\/ ldap:\/\/\/\"/SLAPD_URLS=\"ldapi:\/\/\/ ldap:\/\/\/\ ldaps:\/\/\/\"/g" /etc/sysconfig/slapd + fi + cp /etc/pki/tls/certs/localhost.crt /var/appliance/conf/appliance.chain.pem cp /etc/pki/tls/certs/localhost.crt /var/appliance/conf/appliance.certificate.pem cp /etc/pki/tls/private/localhost.key /var/appliance/conf/appliance.private.key chown ldap /var/appliance/conf/appliance.private.key @@ -63,18 +90,13 @@ if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then modutil -create -dbdir /etc/openldap/certs -force openssl pkcs12 -inkey /var/appliance/conf/appliance.private.key -in /var/appliance/conf/appliance.certificate.pem -export -out /tmp/openldap.p12 -nodes -name 'LDAP-Certificate' -password pass: certutil -A -d /etc/openldap/certs -n "CA Chain" -t CT,, -a -i /var/appliance/conf/appliance.chain.pem - pk12util -i /tmp/openldap.p12 -d /etc/openldap/certs -W "" - rm /tmp/openldap.p12 + expect -c 'log_user 0; spawn pk12util -i /tmp/openldap.p12 -d /etc/openldap/certs -W ""; expect "Enter new password: "; send "netsuslp\r"; expect "Re-enter password: "; send "netsuslp\r"' + rm -f /tmp/openldap.p12 chown -R ldap:ldap /etc/openldap/certs/ fi +log "OK" +log "Finished deploying the LDAP Proxy" - - - -logEvent "OK" - -logEvent "Finished deploying the LDAP Proxy" - -exit 0 +exit 0 \ No newline at end of file diff --git a/NetBoot/netbootInstall.sh b/NetBoot/netbootInstall.sh index 032bb9a..da3ec38 100644 --- a/NetBoot/netbootInstall.sh +++ b/NetBoot/netbootInstall.sh @@ -1,111 +1,308 @@ #!/bin/bash -# This script controls the flow of the SUS installation -pathToScript=$0 -detectedOS=$1 +# This script controls the flow of the NetBoot installation -# Logger -source logger.sh +log "Starting NetBoot Installation" -logEvent "Starting NetBoot Installation" -if [[ $detectedOS == 'Ubuntu' ]]; then - apt-get -qq -y install samba >> $logFile - apt-get -qq -y install tftpd-hpa >> $logFile - apt-get -qq -y install openbsd-inetd >> $logFile - apt-get -qq -y install netatalk >> $logFile +apt_install() { + if [[ $(apt-cache -n search ^${1}$ | awk '{print $1}' | grep ^${1}$) == "$1" ]] && [[ $(dpkg -s $1 2>&- | awk '/Status: / {print $NF}') != "installed" ]]; then + apt-get -qq -y install $1 >> $logFile 2>&1 + if [[ $? -ne 0 ]]; then + exit 1 + fi + fi +} + +yum_install() { + if yum -q list $1 &>- && [[ $(rpm -qa $1) == "" ]] ; then + yum install $1 -y -q >> $logFile 2>&1 + if [[ $? -ne 0 ]]; then + exit 1 + fi + fi +} + +# Install required software +if [[ $(which apt-get 2>&-) != "" ]]; then + export DEBIAN_FRONTEND=noninteractive + echo "samba-common samba-common/do_debconf boolean false" | debconf-set-selections + apt_install samba + unset DEBIAN_FRONTEND + apt_install tftpd-hpa + # apt_install openbsd-inetd + apt_install netatalk + apt_install nfs-kernel-server + apt_install python-configparser +elif [[ $(which yum 2>&-) != "" ]]; then + yum_install avahi + yum_install samba + yum_install samba-client + yum_install tftp-server + release=$(rpm -q --queryformat '%{RELEASE}' rpm | cut -d '.' -f 2) + if [[ $release == "el6" ]] && [[ $(rpm -qa netatalk) == "" ]]; then + yum localinstall ./resources/netatalk-2.2.0-2.el6.x86_64.rpm -y -q >> $logFile + if [[ $? -ne 0 ]]; then + exit 1 + fi + fi + if [[ $release == "el7" ]] && [[ $(rpm -qa libdb4) == "" ]]; then + yum localinstall ./resources/libdb4-4.8.30-21.fc26.x86_64.rpm -y -q >> $logFile + if [[ $? -ne 0 ]]; then + exit 1 + fi + yum localinstall ./resources/netatalk-2.2.3-9.fc20.x86_64.rpm -y -q >> $logFile + if [[ $? -ne 0 ]]; then + exit 1 + fi + sed -i 's/.*- -tcp -noddp -uamlist uams_dhx.so.*/- -tcp -noddp -uamlist uams_dhx.so,uams_dhx2_passwd.so/' /etc/netatalk/afpd.conf + fi + yum_install nfs-utils + yum_install vim-common + chkconfig messagebus on >> $logFile 2>&1 + chkconfig avahi-daemon on >> $logFile 2>&1 + chkconfig rpcbind on >> $logFile 2>&1 + service messagebus start >> $logFile 2>&1 + service avahi-daemon start >> $logFile 2>&1 + service rpcbind start >> $logFile 2>&1 fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - if ! rpm -qa "*db47*" | grep -q "db47" ; then - yum install compat-db47 -y -q >> $logFile +# Prepare the firewall in case it is enabled later +if [[ $(which ufw 2>&-) != "" ]]; then + # HTTP + ufw allow 80/tcp >> $logFile + # SMB + ufw allow 139/tcp >> $logFile + ufw allow 445/tcp >> $logFile + # AFP + ufw allow 548/tcp >> $logFile + # DHCP + ufw allow 67/udp >> $logFile + # TFTP + ufw allow 69/udp >> $logFile + # NFS + ufw allow 111/tcp >> $logFile + ufw allow 111/udp >> $logFile + ufw allow 892/tcp >> $logFile + ufw allow 892/udp >> $logFile + ufw allow 2049/tcp >> $logFile + ufw allow 2049/udp >> $logFile + ufw allow 32769/udp >> $logFile + ufw allow 32803/tcp >> $logFile +elif [[ $(which firewall-cmd 2>&-) != "" ]]; then + # HTTP + firewall-cmd --zone=public --add-port=80/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=80/tcp --permanent >> $logFile 2>&1 + # SMB + firewall-cmd --zone=public --add-port=139/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=139/tcp --permanent >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=445/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=445/tcp --permanent >> $logFile 2>&1 + # AFP + firewall-cmd --zone=public --add-port=548/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=548/tcp --permanent >> $logFile 2>&1 + # DHCP + firewall-cmd --zone=public --add-port=67/udp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=67/udp --permanent >> $logFile 2>&1 + # TFTP + firewall-cmd --zone=public --add-port=69/udp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=69/udp --permanent >> $logFile 2>&1 + # NFS + firewall-cmd --zone=public --add-port=111/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=111/tcp --permanent >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=111/udp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=111/udp --permanent >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=892/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=892/tcp --permanent >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=892/udp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=892/udp --permanent >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=2049/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=2049/tcp --permanent >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=2049/udp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=2049/udp --permanent >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=32769/udp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=32769/udp --permanent >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=32803/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=32803/tcp --permanent >> $logFile 2>&1 +else + # HTTP + if iptables -L | grep DROP | grep -v 'tcp dpt:https' | grep -q 'tcp dpt:http' ; then + iptables -D INPUT -p tcp --dport 80 -j DROP fi - if ! rpm -qa "perl" | grep -q "perl" ; then - yum install perl -y -q >> $logFile + if ! iptables -L | grep ACCEPT | grep -v 'tcp dpt:https' | grep -q 'tcp dpt:http' ; then + iptables -I INPUT -p tcp --dport 80 -j ACCEPT fi - cp ./var/appliance/netatalk-2.2.0-2.el6.x86_64.rpm /var/appliance/netatalk-2.2.0-2.el6.x86_64.rpm - if ! rpm -qa "netatalk" | grep -q "netatalk" ; then - rpm -i -v "/var/appliance/netatalk-2.2.0-2.el6.x86_64.rpm" >> $logFile + # SMB + if iptables -L | grep DROP | grep -q 'tcp dpt:netbios-ssn' ; then + iptables -D INPUT -p tcp --dport 139 -j DROP fi - if ! rpm -qa "avahi" | grep -q "avahi" ; then - yum install avahi -y -q >> $logFile + if ! iptables -L | grep ACCEPT | grep -q 'tcp dpt:netbios-ssn' ; then + iptables -I INPUT -p tcp --dport 139 -j ACCEPT fi - if ! rpm -qa "samba" | grep -q "samba" ; then - yum install samba -y -q >> $logFile + if iptables -L | grep DROP | grep -q 'tcp dpt:microsoft-ds' ; then + iptables -D INPUT -p tcp --dport 445 -j DROP fi - if ! rpm -qa "samba-client" | grep -q "samba-client" ; then - yum install samba-client -y -q >> $logFile + if ! iptables -L | grep ACCEPT | grep -q 'tcp dpt:microsoft-ds' ; then + iptables -I INPUT -p tcp --dport 445 -j ACCEPT fi - if ! rpm -qa "tftp-server" | grep -q "tftp-server" ; then - yum install tftp-server -y -q >> $logFile + # AFP + if iptables -L | grep DROP | grep -q 'tcp dpt:afpovertcp' ; then + iptables -D INPUT -p tcp --dport 548 -j DROP fi - if ! rpm -qa "vim-common" | grep -q "vim-common" ; then - yum install vim-common -y -q >> $logFile + if ! iptables -L | grep ACCEPT | grep -q 'tcp dpt:afpovertcp' ; then + iptables -I INPUT -p tcp --dport 548 -j ACCEPT fi - chkconfig netatalk on - chkconfig smb on - chkconfig tftp on - service smb start - service xinetd start - service messagebus start - service avahi-daemon start - service netatalk start - sed -i 's:/var/lib/tftpboot:/srv/NetBoot/NetBootSP0:' /etc/xinetd.d/tftp - sed -i "s:disable\t\t\t= yes:disable\t\t\t= no:" /etc/xinetd.d/tftp + # DHCP + if iptables -L | grep DROP | grep -q 'udp dpt:bootps' ; then + iptables -D INPUT -p udp --dport 67 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'udp dpt:bootps' ; then + iptables -I INPUT -p udp --dport 67 -j ACCEPT + fi + # TFTP + if iptables -L | grep DROP | grep -q 'udp dpt:tftp' ; then + iptables -D INPUT -p udp --dport 69 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'udp dpt:tftp' ; then + iptables -I INPUT -p udp --dport 69 -j ACCEPT + fi + # NFS + if iptables -L | grep DROP | grep -q 'tcp dpt:sunrpc' ; then + iptables -D INPUT -p tcp --dport 111 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'tcp dpt:sunrpc' ; then + iptables -I INPUT -p tcp --dport 111 -j ACCEPT + fi + if iptables -L | grep DROP | grep -q 'udp dpt:sunrpc' ; then + iptables -D INPUT -p udp --dport 111 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'udp dpt:sunrpc' ; then + iptables -I INPUT -p udp --dport 111 -j ACCEPT + fi + if iptables -L | grep DROP | grep -q 'tcp dpt:892' ; then + iptables -D INPUT -p tcp --dport 892 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'tcp dpt:892' ; then + iptables -I INPUT -p tcp --dport 892 -j ACCEPT + fi + if iptables -L | grep DROP | grep -q 'udp dpt:892' ; then + iptables -D INPUT -p udp --dport 892 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'udp dpt:892' ; then + iptables -I INPUT -p udp --dport 892 -j ACCEPT + fi + if iptables -L | grep DROP | grep -q 'tcp dpt:nfs' ; then + iptables -D INPUT -p tcp --dport 2049 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'tcp dpt:nfs' ; then + iptables -I INPUT -p tcp --dport 2049 -j ACCEPT + fi + if iptables -L | grep DROP | grep -q 'udp dpt:nfs' ; then + iptables -D INPUT -p udp --dport 2049 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'udp dpt:nfs' ; then + iptables -I INPUT -p udp --dport 2049 -j ACCEPT + fi + if iptables -L | grep DROP | grep -q 'udp dpt:filenet-rpc' ; then + iptables -D INPUT -p udp --dport 32769 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'udp dpt:filenet-rpc' ; then + iptables -I INPUT -p udp --dport 32769 -j ACCEPT + fi + if iptables -L | grep DROP | grep -q 'tcp dpt:32803' ; then + iptables -D INPUT -p tcp --dport 32803 -j DROP + fi + if ! iptables -L | grep ACCEPT | grep -q 'tcp dpt:32803' ; then + iptables -I INPUT -p tcp --dport 32803 -j ACCEPT + fi + service iptables save >> $logFile 2>&1 fi -if [ ! -d "/var/db" ]; then - mkdir /var/db +# Configure tftp +if [ -f "/etc/default/tftpd-hpa" ]; then + sed -i 's:/var/lib/tftpboot:/srv/NetBoot/NetBootSP0:' /etc/default/tftpd-hpa + sed -i 's:TFTP_OPTIONS=.*:TFTP_OPTIONS="--secure --blocksize 1460":' /etc/default/tftpd-hpa +fi +if [ -f "/etc/xinetd.d/tftp" ]; then + sed -i 's:/var/lib/tftpboot:/srv/NetBoot/NetBootSP0:' /etc/xinetd.d/tftp + sed -i '/server_args/ s/= -s/= --blocksize 1460 -s/' /etc/xinetd.d/tftp + sed -i '/disable/ s/yes/no/' /etc/xinetd.d/tftp +fi +if [ -f "/usr/lib/systemd/system/tftp.service" ]; then + sed -i 's:ExecStart=.*:ExecStart=/usr/sbin/in.tftpd --blocksize 1460 -s /srv/NetBoot/NetBootSP0:' /usr/lib/systemd/system/tftp.service +fi +if [ -f "/lib/systemd/system/tftp.service" ]; then + sed -i 's:ExecStart=.*:ExecStart=/usr/sbin/in.tftpd --blocksize 1460 -s /srv/NetBoot/NetBootSP0:' /lib/systemd/system/tftp.service +fi +if [ -f "/usr/lib/systemd/system/tftp.service" ] || [ -f "/lib/systemd/system/tftp.service" ]; then + systemctl daemon-reload fi +# Create netboot directories if [ ! -d "/srv/NetBoot/NetBootSP0" ]; then mkdir -p /srv/NetBoot/NetBootSP0 fi - if [ ! -d "/srv/NetBootClients" ]; then mkdir /srv/NetBootClients fi +# Install and configure dhcp killall dhcpd >> $logFile 2>&1 -if [[ $detectedOS == 'Ubuntu' ]]; then - cp -R ./etc/* /etc/ +if [ ! -d "/var/appliance/conf" ]; then + mkdir -p /var/appliance/conf +fi +cp ./resources/dhcpd.conf /var/appliance/conf/ >> $logFile +cp ./resources/configurefornetboot /var/appliance/ >> $logFile + +if [ ! -d "/var/db" ]; then + mkdir /var/db +fi +touch /var/db/dhcpd.leases +cp ./resources/dhcp/* /usr/local/sbin/ >> $logFile + +# Update netatalk configuration +if [ -f "/etc/default/netatalk" ]; then + sed -i 's:.*ATALK_NAME=.*:ATALK_NAME=`/bin/hostname --short`:' /etc/default/netatalk + sed -i 's:.*AFPD_MAX_CLIENTS=.*:AFPD_MAX_CLIENTS=200:' /etc/default/netatalk + sed -i 's:.*AFPD_GUEST=.*:AFPD_GUEST=nobody:' /etc/default/netatalk + sed -i 's:.*ATALKD_RUN=.*:ATALKD_RUN=no:' /etc/default/netatalk + sed -i 's:.*PAPD_RUN=.*:PAPD_RUN=no:' /etc/default/netatalk + sed -i 's:.*TIMELORD_RUN=.*:TIMELORD_RUN=no:' /etc/default/netatalk + sed -i 's:.*A2BOOT_RUN=.*:A2BOOT_RUN=yes:' /etc/default/netatalk + sed -i 's:.*ATALK_BGROUND=.*:ATALK_BGROUND=no:' /etc/default/netatalk + sed -i '/"NetBoot"/d' /etc/netatalk/AppleVolumes.default + sed -i '/End of File/d' /etc/netatalk/AppleVolumes.default + echo '# End of File' >> /etc/netatalk/AppleVolumes.default + sed -i '/End of File/ i\ +/srv/NetBootClients/$i "NetBoot" allow:afpuser rwlist:afpuser options:upriv preexec:"mkdir -p /srv/NetBootClients/$i/NetBoot001" postexec:"rm -rf /srv/NetBootClients/$i"' /etc/netatalk/AppleVolumes.default fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - # Configure netatalk +if [ -f "/etc/netatalk/netatalk.conf" ]; then if ! grep -q '\- \-setuplog "default log_info /var/log/afpd.log"' /etc/netatalk/afpd.conf; then echo '- -setuplog "default log_info /var/log/afpd.log"' >> /etc/netatalk/afpd.conf fi - # Remove any entries from old installations + sed -i 's:.*ATALK_NAME=.*:ATALK_NAME=`/bin/hostname --short`:' /etc/netatalk/netatalk.conf + sed -i 's:.*AFPD_MAX_CLIENTS=.*:AFPD_MAX_CLIENTS=200:' /etc/netatalk/netatalk.conf + sed -i 's:.*AFPD_GUEST=.*:AFPD_GUEST=nobody:' /etc/netatalk/netatalk.conf + sed -i 's:.*ATALKD_RUN=.*:ATALKD_RUN=no:' /etc/netatalk/netatalk.conf + sed -i 's:.*PAPD_RUN=.*:PAPD_RUN=no:' /etc/netatalk/netatalk.conf + sed -i 's:.*TIMELORD_RUN=.*:TIMELORD_RUN=no:' /etc/netatalk/netatalk.conf + sed -i 's:.*A2BOOT_RUN=.*:A2BOOT_RUN=yes:' /etc/netatalk/netatalk.conf + sed -i 's:.*ATALK_BGROUND=.*:ATALK_BGROUND=no:' /etc/netatalk/netatalk.conf sed -i '/"NetBoot"/d' /etc/netatalk/AppleVolumes.default - echo '/srv/NetBootClients/$i "NetBoot" allow:afpuser rwlist:afpuser options:upriv cnidscheme:dbd ea:sys preexec:"mkdir -p /srv/NetBootClients/$i/NetBoot001" postexec:"rm -rf /srv/NetBootClients/$i"' >> /etc/netatalk/AppleVolumes.default - sed -i 's/#AFPD_MAX_CLIENTS=.*/AFPD_MAX_CLIENTS=200/' /etc/netatalk/netatalk.conf - sed -i 's:#ATALK_NAME=.*:ATALK_NAME=`/bin/hostname --short`:' /etc/netatalk/netatalk.conf - sed -i 's/#AFPD_GUEST=.*/AFPD_GUEST=nobody/' /etc/netatalk/netatalk.conf - sed -i 's/#ATALKD_RUN=.*/ATALKD_RUN=no/' /etc/netatalk/netatalk.conf - sed -i 's/#PAPD_RUN=.*/PAPD_RUN=no/' /etc/netatalk/netatalk.conf - sed -i 's/#TIMELORD_RUN=.*/TIMELORD_RUN=no/' /etc/netatalk/netatalk.conf - sed -i 's/#A2BOOT_RUN=.*/A2BOOT_RUN=yes/' /etc/netatalk/netatalk.conf - sed -i 's/#ATALK_BGROUND=.*/ATALK_BGROUND=no/' /etc/netatalk/netatalk.conf -fi -cp -R ./usr/* /usr/ -cp -R ./var/* /var/ - -#Create Apache Share for NetBoot -if [[ $detectedOS == 'Ubuntu' ]]; then - # Remove any entries from old installations - if [ -f "/etc/apache2/sites-enabled/000-default" ]; then - sed -i '/[[:space:]]*Alias \/NetBoot\/ "\/srv\/NetBoot\/"/,/[[:space:]]*<\/Directory>/d' /etc/apache2/sites-enabled/000-default - - sed -i "s''\tAlias /NetBoot/ \"/srv/NetBoot/\"\n\t\n\t\tOptions Indexes FollowSymLinks MultiViews\n\t\tAllowOverride None\n\t\tOrder allow,deny\n\t\tallow from all\n\t\n'g" /etc/apache2/sites-enabled/000-default - fi - if [ -f "/etc/apache2/sites-enabled/000-default.conf" ]; then - sed -i '/[[:space:]]*Alias \/NetBoot\/ "\/srv\/NetBoot\/"/,/[[:space:]]*<\/Directory>/d' /etc/apache2/sites-enabled/000-default.conf - - sed -i "s''\tAlias /NetBoot/ \"/srv/NetBoot/\"\n\t\n\t\tOptions Indexes FollowSymLinks MultiViews\n\t\tAllowOverride None\n\t\tRequire all granted\n\t\n'g" /etc/apache2/sites-enabled/000-default.conf - fi -fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then + sed -i '/End of File/d' /etc/netatalk/AppleVolumes.default + echo '# End of File' >> /etc/netatalk/AppleVolumes.default + sed -i '/End of File/ i\ +/srv/NetBootClients/$i "NetBoot" allow:afpuser rwlist:afpuser options:upriv cnidscheme:dbd ea:sys preexec:"mkdir -p /srv/NetBootClients/$i/NetBoot001" postexec:"rm -rf /srv/NetBootClients/$i"' /etc/netatalk/AppleVolumes.default +fi + +# Create Apache Share for NetBoot +if [ -f "/etc/apache2/sites-enabled/000-default.conf" ]; then + # Remove any entries from old installations + sed -i '/[[:space:]]*Alias \/NetBoot\/ "\/srv\/NetBoot\/"/,/[[:space:]]*<\/Directory>/d' /etc/apache2/sites-enabled/000-default.conf + sed -i "s''\tAlias /NetBoot/ \"/srv/NetBoot/\"\n\t\n\t\tOptions Indexes FollowSymLinks MultiViews\n\t\tAllowOverride None\n\t\tRequire all granted\n\t\n'g" /etc/apache2/sites-enabled/000-default.conf +fi +if [ -f "/etc/httpd/conf/httpd.conf" ]; then # Remove any entries from old installations sed -i '/[[:space:]]*Alias \/NetBoot\/ "\/srv\/NetBoot\/"/,/[[:space:]]*<\/Directory>/d' /etc/httpd/conf/httpd.conf - if httpd -v | grep version | grep '2.2'; then + if httpd -v | grep version | grep -q '2.2'; then echo ' Alias /NetBoot/ "/srv/NetBoot/"' >> /etc/httpd/conf/httpd.conf echo ' @@ -126,17 +323,18 @@ if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then ' >> /etc/httpd/conf/httpd.conf fi fi -#Creates the accounts to be used for the different services -if [ "$(getent passwd smbuser)" ]; then + +# Create the accounts to be used for the different services +if [[ $(getent passwd smbuser) != "" ]]; then echo "smbuser already exists" else - useradd -c 'NetBoot Admin' -d /dev/null -g users -s /sbin/nologin smbuser >> $logFile + useradd -c 'NetBoot Admin' -d /dev/null -g users -s $(which nologin) smbuser >> $logFile 2>&1 echo smbuser:smbuser1 | chpasswd - (echo smbuser1; echo smbuser1) | smbpasswd -s -a smbuser + (echo smbuser1; echo smbuser1) | smbpasswd -s -a smbuser >> $logFile 2>&1 fi -#Needs normal user creation for AFP mount to work proper -if [ "$(getent passwd afpuser)" ]; then +# Needs normal user creation for AFP mount to work properly +if [[ $(getent passwd afpuser) != "" ]]; then echo "afpuser already exists" else useradd -c 'NetBoot User' -d /home/afpuser -g users -m -s /bin/sh afpuser >> $logFile @@ -147,18 +345,48 @@ if [ ! -d "/home/afpuser" ]; then chown afpuser:users /home/afpuser >> $logFile fi -#Change SMB setting for guest access -sed -i "s/map to guest = bad user/map to guest = never/g" /etc/samba/smb.conf +# Configure nfs +if [ -f "/etc/default/nfs-kernel-server" ]; then + sed -i 's/.*RPCMOUNTDOPTS.*/RPCMOUNTDOPTS="--port 892"/' /etc/default/nfs-kernel-server + touch /etc/modprobe.d/lockd.conf + sed -i '/^lockd/d' /etc/modules + echo "lockd" >> /etc/modules +fi +if [ -f "/etc/sysconfig/nfs" ]; then + if grep -q LOCKD_TCPPORT /etc/sysconfig/nfs; then + sed -i 's/.*LOCKD_TCPPORT.*/LOCKD_TCPPORT=32803/' /etc/sysconfig/nfs + sed -i 's/.*LOCKD_UDPPORT.*/LOCKD_UDPPORT=32769/' /etc/sysconfig/nfs + sed -i 's/.*MOUNTD_PORT.*/MOUNTD_PORT=892/' /etc/sysconfig/nfs + else + sed -i 's/.*RPCMOUNTDOPTS.*/RPCMOUNTDOPTS="-p 892"/' /etc/sysconfig/nfs + fi +fi +if [ -f "/etc/modprobe.d/lockd.conf" ]; then + if ! grep -q nlm_tcpport /etc/modprobe.d/lockd.conf; then + echo "options lockd nlm_tcpport=32803" >> /etc/modprobe.d/lockd.conf + fi + sed -i 's/.*nlm_tcpport.*/options lockd nlm_tcpport=32803/' /etc/modprobe.d/lockd.conf + if ! grep -q nlm_udpport /etc/modprobe.d/lockd.conf; then + echo "options lockd nlm_udpport=32769" >> /etc/modprobe.d/lockd.conf + fi + sed -i 's/.*nlm_udpport.*/options lockd nlm_udpport=32769/' /etc/modprobe.d/lockd.conf +fi +sed -i "/NetBootSP0/d" /etc/exports +echo "/srv/NetBoot/NetBootSP0 *(ro,no_subtree_check,no_root_squash,insecure)" >> "/etc/exports" +exportfs -a -#Change SMB settings to allow for a symlink in an app or pkg +# Configure samba +# Change SMB setting for guest access +sed -i "s/map to guest = bad user/map to guest = never/g" /etc/samba/smb.conf +# Change SMB settings to allow for a symlink in an app or pkg if ! grep -q 'unix extensions' /etc/samba/smb.conf ; then sed -i '/\[global\]/ a\\tunix extensions = no' /etc/samba/smb.conf fi -#Change SMB setting to eliminate CUPS errors +# Change SMB setting to eliminate CUPS errors sed -i 's:;\tprintcap name = lpstat:\tprintcap name = /dev/null:' /etc/samba/smb.conf sed -i 's/;\tprinting = cups/\tprinting = bsd/' /etc/samba/smb.conf -#Create the SMB share for NetBoot +# Create the SMB share for NetBoot if ! grep -q '\[NetBoot\]' /etc/samba/smb.conf ; then mkdir -p /etc/samba/conf.d printf '\t[NetBoot] @@ -180,13 +408,14 @@ printf ' ' >> /etc/samba/smb.conf fi +# Make the smbuser the owner of the NetBootSP0 share chown smbuser /srv/NetBoot/NetBootSP0 >> $logFile -#Make the afpuser the owner of the NetBootClients share +# Make the afpuser the owner of the NetBootClients share chown afpuser /srv/NetBootClients >> $logFile -logEvent "OK" +log "OK" -logEvent "Finished deploying NetBoot" +log "Finished deploying NetBoot" -exit 0 +exit 0 \ No newline at end of file diff --git a/NetBoot/var/appliance/configurefornetboot b/NetBoot/var/appliance/configurefornetboot index 4d5ec14..49cbf68 100755 --- a/NetBoot/var/appliance/configurefornetboot +++ b/NetBoot/var/appliance/configurefornetboot @@ -22,9 +22,9 @@ ip=`ip addr show to 0.0.0.0/0 scope global | awk '/[[:space:]]inet / { print gensub("/.*","","g",$2) }'` ipdec=`awk -v dec=${ip} 'BEGIN{n=split(dec,d,".");for(i=1;i<=n;i++) printf ":%02X",d[i];print ""}'` -imageid=`cat /etc/dhcpd.conf | grep "option vendor-encapsulated-options 01:01:01:04:02:FF:FF:07:04" | sed 's/option vendor-encapsulated-options 01:01:01:04:02:FF:FF:07:04://g' | sed 's/ //g' | sed 's/'$'\t''//g' | cut -c1-11` -curafp=`cat /etc/dhcpd.conf | grep "01:01:02:08:04:.*.:80" | sed 's/option vendor-encapsulated-options 01:01:02:08:04:.*.:80:.*:61:66:70:75:73:65:72:3A://g' | awk -F40 '{print $1}' | tr -d ' ' | sed 's/\(.*\)./\1/'` -afppw=`cat /etc/dhcpd.conf | grep "01:01:02:08:04:.*.:80" | sed 's/option vendor-encapsulated-options 01:01:02:08:04:.*.:80:.*:61:66:70:75:73:65:72:3A://g' | sed 's/://g' | awk -F40 '{print $1}' | tr -d ' ' | wc -c` +imageid=`grep 'FF:FF:07:04' /etc/dhcpd.conf | sed 's/.*FF:FF:07:04://g' | cut -c1-11` +curafp=`grep "01:01:02:08:04:.*.:80" /etc/dhcpd.conf | sed 's/option vendor-encapsulated-options 01:01:02:08:04:.*.:80:.*:61:66:70:75:73:65:72:3A://g' | awk -F40 '{print $1}' | tr -d ' ' | sed 's/\(.*\)./\1/'` +afppw=`grep "01:01:02:08:04:.*.:80" /etc/dhcpd.conf | sed 's/option vendor-encapsulated-options 01:01:02:08:04:.*.:80:.*:61:66:70:75:73:65:72:3A://g' | sed 's/://g' | awk -F40 '{print $1}' | tr -d ' ' | wc -c` afppwlen=`expr ${afppw} / 2` iphex=`echo ${ip} | xxd -c 1 -ps -u | tr '\n' ':' | sed 's/0A://g' | sed 's/\(.*\)./\1/'` num=`echo ${iphex} | sed 's/://g' | wc -c` @@ -34,11 +34,12 @@ num=`expr ${num} + ${afppwlen}` lengthhex=`awk -v dec=${num} 'BEGIN { n=split(dec,d,"."); for(i=1;i<=n;i++) printf ":%02X",d[i]; print "" }'` -sed -i "s/01:01:02:08:04:${imageid}:80:.*/01:01:02:08:04:${imageid}:80${lengthhex}:61:66:70:3A:2F:2F:61:66:70:75:73:65:72:3A:${curafp}:40:${iphex}:2F:4E:65:74:42:6F:6F:74:81:11:4E:65:74:42:6F:6F:74:30:30:31:2F:53:68:61:64:6F:77;/g" /etc/dhcpd.conf +sed -i "s/01:01:02:08:04:${imageid}:80:.*/01:01:02:08:04:${imageid}:80${lengthhex}:61:66:70:3A:2F:2F:61:66:70:75:73:65:72:3A:${curafp}:40:${iphex}:2F:4E:65:74:42:6F:6F:74:81:11:4E:65:74:42:6F:6F:74:30:30:31:2F:53:68:61:64:6F:77;/" /etc/dhcpd.conf sed -i "s/7, 12) = 08:04:${imageid}:03:04.*)/7, 12) = 08:04:${imageid}:03:04${ipdec})/g" /etc/dhcpd.conf sed -i "s/7, 12) = 03:04.*:08:04:${imageid})/7, 12) = 03:04${ipdec}:08:04:${imageid})/g" /etc/dhcpd.conf sed -i "s/next-server.*;/next-server ${ip};/g" /etc/dhcpd.conf -sed -i "s/http:\/\/.*\/NetBoot\/NetBootSP0\//http:\/\/${ip}\/NetBoot\/NetBootSP0\//g" /etc/dhcpd.conf +sed -i "s|nfs:.*:/srv/NetBoot/NetBootSP0:|nfs:${ip}:/srv/NetBoot/NetBootSP0:|" /etc/dhcpd.conf +sed -i "s|http://.*/NetBoot/NetBootSP0/|http://${ip}/NetBoot/NetBootSP0/|" /etc/dhcpd.conf killall dhcpd diff --git a/NetBoot/var/appliance/libdb4-4.8.30-21.fc26.x86_64.rpm b/NetBoot/var/appliance/libdb4-4.8.30-21.fc26.x86_64.rpm new file mode 100644 index 0000000..78ef646 Binary files /dev/null and b/NetBoot/var/appliance/libdb4-4.8.30-21.fc26.x86_64.rpm differ diff --git a/NetBoot/var/appliance/netatalk-2.2.3-9.fc20.x86_64.rpm b/NetBoot/var/appliance/netatalk-2.2.3-9.fc20.x86_64.rpm new file mode 100644 index 0000000..a15e500 Binary files /dev/null and b/NetBoot/var/appliance/netatalk-2.2.3-9.fc20.x86_64.rpm differ diff --git a/README.md b/README.md index daa56ff..1a9597a 100755 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ # NetSUS Downloads Installer: -https://github.com/jamf/NetSUS/releases/download/4.1.0/NetSUSLPInstaller_4.1.0.run.zip +[https://github.com/jamf/NetSUS/releases/download/4.2.1/NetSUSLPInstaller_4.2.1.run.zip](https://www.dropbox.com/s/c3aeii38qh6c05y/NetSUSLPInstaller_4.2.1.run?dl=0) OVA: -https://github.com/jamf/NetSUS/releases/download/4.1.0/NetSUSLP_4.1.0.ova.zip +[https://github.com/jamf/NetSUS/releases/download/4.2.1/NetSUSLP_4.2.1.ova.zip](https://www.dropbox.com/s/zfkmsqx1cuope79/NetSUSLP_4.2.1.ova?dl=0) # What is NetSUS? @@ -30,23 +30,22 @@ For a getting started guide and step-by-step walkthroughs check out the **[docum #### Supported Linux distributions: -* Ubuntu 10.04 LTS Server -* Ubuntu 12.04 LTS Server * Ubuntu 14.04 LTS Server +* Ubuntu 16.04 LTS Server * Red Hat Enterprise Linux (RHEL) 6.4 or later * CentOS 6.4 or later #### To install the NetBoot/SUS/LP server using an installer, you need: * The NetBoot/SUS/LP Server Installer (.run), available at: - + * 500 GB of disk space available * 1 GB of RAM #### To set up the NetBoot/SUS/LP server as an appliance, you need: * The OVA file for the NetBoot/SUS/LP server, available at: - + * Virtualization software that supports Open Virtualization Format * 500 GB of disk space available * 2 GB of RAM diff --git a/SUS/susInstall.sh b/SUS/susInstall.sh index bbdfea3..030d5e3 100644 --- a/SUS/susInstall.sh +++ b/SUS/susInstall.sh @@ -1,174 +1,157 @@ #!/bin/bash # This script controls the flow of the SUS installation -pathToScript=$0 -detectedOS=$1 -# Logger -source logger.sh +log "Starting SUS Installation" -logEvent "Starting SUS Installation" - -if [[ $detectedOS == 'Ubuntu' ]]; then - apt-get -qq -y install php5 >> $logFile - apt-get -qq -y install curl >> $logFile +apt_install() { + if [[ $(apt-cache -n search ^${1}$ | awk '{print $1}' | grep ^${1}$) == "$1" ]] && [[ $(dpkg -s $1 2>&- | awk '/Status: / {print $NF}') != "installed" ]]; then + apt-get -qq -y install $1 >> $logFile 2>&1 + if [[ $? -ne 0 ]]; then + exit 1 + fi + fi +} + +yum_install() { + if yum -q list $1 &>- && [[ $(rpm -qa $1) == "" ]] ; then + yum install $1 -y -q >> $logFile 2>&1 + if [[ $? -ne 0 ]]; then + exit 1 + fi + fi +} + +# Install required software +if [[ $(which apt-get 2>&-) != "" ]]; then + apt_install libapache2-mod-php5 + apt_install libapache2-mod-php + apt_install php-xml + apt_install curl +elif [[ $(which yum 2>&-) != "" ]]; then + yum_install mod_ssl + yum_install php + yum_install php-xml fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - if ! rpm -qa "mod_ssl" | grep -q "mod_ssl" ; then - yum install mod_ssl -y -q >> $logFile +# Prepare the firewall in case it is enabled later +if [[ $(which ufw 2>&-) != "" ]]; then + # HTTP + ufw allow 80/tcp >> $logFile +elif [[ $(which firewall-cmd 2>&-) != "" ]]; then + # HTTP + firewall-cmd --zone=public --add-port=80/tcp >> $logFile 2>&1 + firewall-cmd --zone=public --add-port=80/tcp --permanent >> $logFile 2>&1 +else + # HTTP + if iptables -L | grep DROP | grep -v 'tcp dpt:https' | grep -q 'tcp dpt:http' ; then + iptables -D INPUT -p tcp --dport 80 -j DROP fi - if ! rpm -qa "php" | grep -q "php" ; then - yum install php -y -q >> $logFile - fi - if ! rpm -qa "php-xml" | grep -q "php-xml" ; then - yum install php-xml -y -q >> $logFile + if ! iptables -L | grep ACCEPT | grep -v 'tcp dpt:https' | grep -q 'tcp dpt:http' ; then + iptables -I INPUT -p tcp --dport 80 -j ACCEPT fi + service iptables save >> $logFile 2>&1 fi - +# Create SUS directories +if [ ! -d "/var/appliance" ]; then + mkdir /var/appliance +fi if [ ! -d "/var/lib/reposado" ]; then - mkdir /var/lib/reposado + mkdir /var/lib/reposado fi - if [ ! -d "/srv/SUS/metadata" ]; then mkdir -p /srv/SUS/metadata fi - if [ ! -d "/srv/SUS/html/content/catalogs" ]; then mkdir -p /srv/SUS/html/content/catalogs fi -cp -R ./var/* /var/ +# Install reposado +cp ./resources/sus_sync.py /var/appliance/ >> $logFile 2>&1 +cp -R ./resources/reposado/* /var/lib/reposado/ >> $logFile 2>&1 -#Set perms on SUS sync +# Set perms on SUS sync chmod +x /var/appliance/sus_sync.py # Enable apache rewrite rules -if [[ $detectedOS == 'Ubuntu' ]]; then - a2enmod rewrite >> $logFile -fi -#Point Apache to SUS -#TODO - This will not take into account if the installer is run again - -if [[ $detectedOS == 'Ubuntu' ]]; then - if [ -f "/etc/apache2/sites-enabled/000-default" ]; then - sed -i "s/DocumentRoot.*/DocumentRoot \/srv\/SUS\/html\//g" /etc/apache2/sites-enabled/000-default - fi - if [ -f "/etc/apache2/sites-enabled/000-default.conf" ]; then - sed -i "s/DocumentRoot.*/DocumentRoot \/srv\/SUS\/html\//g" /etc/apache2/sites-enabled/000-default.conf - sed -i '/[[:space:]]*/d' /etc/apache2/sites-enabled/000-default.conf - sed -i "s''\t\n\t\tOptions Indexes FollowSymLinks MultiViews\n\t\tAllowOverride None\n\t\tRequire all granted\n\t\n'g" /etc/apache2/sites-enabled/000-default.conf - fi +if [[ $(which a2enmod 2>&-) != "" ]]; then + a2enmod rewrite >> $logFile fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - # Remove any entries from old installations - sed -i 's:/srv/SUS/html:/var/www/html:' /etc/httpd/conf/httpd.conf - sed -i '/{HTTP_USER_AGENT} Darwin/d' /etc/httpd/conf/httpd.conf - sed -i '/sucatalog/d' /etc/httpd/conf/httpd.conf - sed -i 's/\/var\/www\/html/\/srv\/SUS\/html/' /etc/httpd/conf/httpd.conf -fi -if [[ $detectedOS == 'Ubuntu' ]]; then -if [ -f "/etc/apache2/sites-enabled/000-default" ]; then - sed -i "s|||" /etc/apache2/sites-enabled/000-default - # Remove any entries from old installations - sed -i '/{HTTP_USER_AGENT} Darwin/d' /etc/apache2/sites-enabled/000-default - sed -i '/sucatalog/d' /etc/apache2/sites-enabled/000-default - - - cat >>/etc/apache2/sites-enabled/000-default < - RewriteEngine On - RewriteCond %{HTTP_USER_AGENT} Darwin/9 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/10 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard-snowleopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/11 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-lion-snowleopard-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/12 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/13 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/14 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/15 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog - - - -ZHEREDOC +# Point Apache to SUS +# TODO - This will not take into account if the installer is run again - # Remove empty sections - sed -i 'N;N;s/\n[[:space:]]*\n[[:space:]]*RewriteEngine On\n[[:space:]]*<\/IfModule>//;P;D' /etc/apache2/sites-enabled/000-default -fi if [ -f "/etc/apache2/sites-enabled/000-default.conf" ]; then + sed -i "s:DocumentRoot.*:DocumentRoot /srv/SUS/html/:g" /etc/apache2/sites-enabled/000-default.conf + sed -i '/[[:space:]]*/d' /etc/apache2/sites-enabled/000-default.conf + sed -i "s''\t\n\t\tOptions Indexes FollowSymLinks MultiViews\n\t\tAllowOverride None\n\t\tRequire all granted\n\t\n'g" /etc/apache2/sites-enabled/000-default.conf sed -i "s|||" /etc/apache2/sites-enabled/000-default.conf - # Remove any entries from old installations sed -i '/{HTTP_USER_AGENT} Darwin/d' /etc/apache2/sites-enabled/000-default.conf sed -i '/sucatalog/d' /etc/apache2/sites-enabled/000-default.conf - - cat >>/etc/apache2/sites-enabled/000-default.conf < - RewriteEngine On - RewriteCond %{HTTP_USER_AGENT} Darwin/9 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/10 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard-snowleopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/11 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-lion-snowleopard-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/12 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/13 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/14 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog - RewriteCond %{HTTP_USER_AGENT} Darwin/15 - RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog - + + RewriteEngine On + RewriteCond %{HTTP_USER_AGENT} Darwin/9 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/10 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard-snowleopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/11 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/12 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/13 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/14 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/15 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/16 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/17 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + ZHEREDOC - # Remove empty sections sed -i 'N;N;s/\n[[:space:]]*\n[[:space:]]*RewriteEngine On\n[[:space:]]*<\/IfModule>//;P;D' /etc/apache2/sites-enabled/000-default.conf fi +if [ -f "/etc/httpd/conf/httpd.conf" ]; then + # Remove any entries from old installations + sed -i 's:/srv/SUS/html:/var/www/html:' /etc/httpd/conf/httpd.conf + sed -i '/{HTTP_USER_AGENT} Darwin/d' /etc/httpd/conf/httpd.conf + sed -i '/sucatalog/d' /etc/httpd/conf/httpd.conf + sed -i 's:/var/www/html:/srv/SUS/html:' /etc/httpd/conf/httpd.conf + cat >>/etc/httpd/conf/httpd.conf < + RewriteEngine On + RewriteCond %{HTTP_USER_AGENT} Darwin/9 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/10 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard-snowleopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/11 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/12 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/13 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/14 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/15 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/16 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + RewriteCond %{HTTP_USER_AGENT} Darwin/17 + RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog + +ZHEREDOC + # Remove empty sections + sed -i 'N;N;s/\n[[:space:]]*\n[[:space:]]*RewriteEngine On\n[[:space:]]*<\/IfModule>//;P;D' /etc/httpd/conf/httpd.conf fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then -# Remove any entries from old installations -sed -i '/{HTTP_USER_AGENT} Darwin/d' /etc/httpd/conf/httpd.conf -sed -i '/sucatalog/d' /etc/httpd/conf/httpd.conf - -echo ' - -RewriteEngine On -RewriteCond %{HTTP_USER_AGENT} Darwin/9 -RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard.merged-1.sucatalog -RewriteCond %{HTTP_USER_AGENT} Darwin/10 -RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-leopard-snowleopard.merged-1.sucatalog -RewriteCond %{HTTP_USER_AGENT} Darwin/11 -RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-lion-snowleopard-leopard.merged-1.sucatalog -RewriteCond %{HTTP_USER_AGENT} Darwin/12 -RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog -RewriteCond %{HTTP_USER_AGENT} Darwin/13 -RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog -RewriteCond %{HTTP_USER_AGENT} Darwin/14 -RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog -RewriteCond %{HTTP_USER_AGENT} Darwin/15 -RewriteRule ^/index\.sucatalog$ http://%{HTTP_HOST}/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog -' >> /etc/httpd/conf/httpd.conf - -# Remove empty sections -sed -i 'N;N;s/\n[[:space:]]*\n[[:space:]]*RewriteEngine On\n[[:space:]]*<\/IfModule>//;P;D' /etc/httpd/conf/httpd.conf -fi - - - -logEvent "OK" - -logEvent "Finished deploying the appliance web application" +log "OK" -exit 0 +log "Finished deploying SUS" +exit 0 \ No newline at end of file diff --git a/SUS/var/lib/reposado/repo_sync b/SUS/var/lib/reposado/repo_sync index 1ded641..3a5226c 100755 --- a/SUS/var/lib/reposado/repo_sync +++ b/SUS/var/lib/reposado/repo_sync @@ -73,7 +73,7 @@ else: os_rename = os.rename def parseServerMetadata(filename): - '''Parses a softwareupdate server metadata file, looking for information + '''Parses a softwareupdate server metadata file, looking for information of interest. Returns a dictionary containing title, version, and description.''' title = '' @@ -85,17 +85,17 @@ def parseServerMetadata(filename): reposadocommon.print_stderr( 'Error reading %s: %s', filename, err) return {} - vers = md_plist.get('CFBundleShortVersionString','') + vers = md_plist.get('CFBundleShortVersionString', '') localization = md_plist.get('localization', {}) languages = localization.keys() preferred_lang = getPreferredLocalization(languages) preferred_localization = localization.get(preferred_lang) if preferred_localization: - title = preferred_localization.get('title','') - encoded_description = preferred_localization.get('description','') + title = preferred_localization.get('title', '') + encoded_description = preferred_localization.get('description', '') if encoded_description: description = str(encoded_description) - + metadata = {} metadata['title'] = title metadata['version'] = vers @@ -104,7 +104,7 @@ def parseServerMetadata(filename): def parse_cdata(cdata_str): - '''Parses the CDATA string from an Apple Software Update distribution file + '''Parses the CDATA string from an Apple Software Update distribution file and returns a dictionary with key/value pairs. The data in the CDATA string is in the format of an OS X .strings file, @@ -117,8 +117,8 @@ def parse_cdata(cdata_str): multiple lines. '; - Values can span multiple lines; either single or double-quotes can be used - to quote the keys and values, and the alternative quote character is allowed + Values can span multiple lines; either single or double-quotes can be used + to quote the keys and values, and the alternative quote character is allowed as a literal inside the other, otherwise the quote character is escaped. //-style comments and blank lines are allowed in the string; these should @@ -127,9 +127,9 @@ def parse_cdata(cdata_str): ''' parsed_data = {} REGEX = (r"""^\s*""" - """(?P['"]?)(?P[^'"]+)(?P=key_quote)""" - """\s*=\s*""" - """(?P['"])(?P.*?)(?P=value_quote);$""") + r"""(?P['"]?)(?P[^'"]+)(?P=key_quote)""" + r"""\s*=\s*""" + r"""(?P['"])(?P.*?)(?P=value_quote);$""") regex = re.compile(REGEX, re.MULTILINE | re.DOTALL) # iterate through the string, finding all possible non-overlapping @@ -163,7 +163,7 @@ def parseSUdist(filename, debug=False): reposadocommon.print_stderr( 'Error reading %s: %s', filename, err) return None - + su_choice_id_key = 'su' # look for > fileobj, 'fail' # throw error if download fails print >> fileobj, 'dump-header -' # dump headers to stdout print >> fileobj, 'speed-time = 30' # give up if too slow d/l - # next line does not work on windows - #print >> fileobj, 'output = "%s"' % tempdownloadpath - - print >> fileobj, 'ciphers = HIGH,!ADH' # use only >=128 bit SSL + print >> fileobj, 'tlsv1' # use only TLS 1.x print >> fileobj, 'url = "%s"' % url - + # add additional options from our prefs if reposadocommon.pref('AdditionalCurlOptions'): for line in reposadocommon.pref('AdditionalCurlOptions'): @@ -314,9 +312,9 @@ def curl(url, destinationpath, onlyifnewer=False, etag=None, resume=False): if os.path.exists(destinationpath): if etag: - escaped_etag = etag.replace('"','\\"') + escaped_etag = etag.replace('"', '\\"') print >> fileobj, ('header = "If-None-Match: %s"' - % escaped_etag) + % escaped_etag) elif onlyifnewer: print >> fileobj, 'time-cond = "%s"' % destinationpath else: @@ -325,12 +323,12 @@ def curl(url, destinationpath, onlyifnewer=False, etag=None, resume=False): fileobj.close() except Exception, err: raise CurlError(-5, 'Error writing curl directive: %s' % str(err)) - + cmd = [reposadocommon.pref('CurlPath'), - '-q', # don't read .curlrc file - '--config', # use config file - curldirectivepath, - '-o', tempdownloadpath] + '-q', # don't read .curlrc file + '--config', # use config file + curldirectivepath, + '-o', tempdownloadpath] proc = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE, @@ -338,25 +336,34 @@ def curl(url, destinationpath, onlyifnewer=False, etag=None, resume=False): targetsize = 0 downloadedpercent = -1 - donewithheaders = False - printed_message = False while True: - if not donewithheaders: - info = proc.stdout.readline().strip('\r\n') - if info: - if info.startswith('HTTP/'): - header['http_result_code'] = info.split(None, 2)[1] - header['http_result_description'] = info.split(None, 2)[2] - elif ': ' in info: - part = info.split(None, 1) + line = proc.stdout.readline() + if line: + line_stripped = line.rstrip('\r\n') + if line_stripped: + line = line_stripped + + if line.startswith('HTTP/'): + header['http_result_code'] = line.split(None, 2)[1] + header['http_result_description'] = line.split(None, 2)[2] + elif ': ' in line: + part = line.split(None, 1) fieldname = part[0].rstrip(':').lower() header[fieldname] = part[1] else: - # we got an empty line; end of headers (or curl exited) - donewithheaders = True + # "empty" line, but not end of output. likely end of headers + # for a given HTTP result section try: targetsize = int(header.get('content-length')) + if (targetsize and + header.get('http_result_code').startswith('2')): + if reposadocommon.pref('HumanReadableSizes'): + printed_size = reposadocommon.humanReadable(targetsize) + else: + printed_size = str(targetsize) + ' bytes' + reposadocommon.print_stdout( + 'Downloading %s from %s...', printed_size, url) except (ValueError, TypeError): targetsize = 0 if header.get('http_result_code') == '206': @@ -371,31 +378,26 @@ def curl(url, destinationpath, onlyifnewer=False, etag=None, resume=False): except (ValueError, TypeError): targetsize = 0 - elif targetsize and header.get('http_result_code').startswith('2'): - if not printed_message: - printed_message = True - reposadocommon.print_stdout('Downloading %s bytes from %s...', - targetsize, url) - time.sleep(0.1) - - if (proc.poll() != None): + elif proc.poll() != None: break retcode = proc.poll() if retcode: - curlerr = proc.stderr.read().rstrip('\n').split(None, 2)[2] + curlerr = proc.stderr.read().rstrip('\n') + if curlerr: + curlerr = curlerr.split(None, 2)[2] if os.path.exists(tempdownloadpath): if (not resume) or (retcode == 33): # 33 means server doesn't support range requests - # and so cannot resume downloads, so + # and so cannot resume downloads, so os.remove(tempdownloadpath) raise CurlError(retcode, curlerr) else: temp_download_exists = os.path.isfile(tempdownloadpath) http_result = header.get('http_result_code') - if downloadedpercent != 100 and \ - http_result.startswith('2') and \ - temp_download_exists: + if (downloadedpercent != 100 and + http_result.startswith('2') and + temp_download_exists): downloadedsize = os.path.getsize(tempdownloadpath) if downloadedsize >= targetsize: os_rename(tempdownloadpath, destinationpath) @@ -405,22 +407,50 @@ def curl(url, destinationpath, onlyifnewer=False, etag=None, resume=False): if not resume and temp_download_exists: os.remove(tempdownloadpath) raise CurlError(-5, 'Expected %s bytes, got: %s' % - (targetsize, downloadedsize)) + (targetsize, downloadedsize)) elif http_result.startswith('2') and temp_download_exists: os_rename(tempdownloadpath, destinationpath) return header elif http_result == '304': return header + elif (not temp_download_exists and + http_result == '200' and + os.path.isfile(destinationpath) and + (not (targetsize and + (targetsize != os.path.getsize(destinationpath))))): + # The above comparison tries to check that a) no body content was + # delivered, b) the HTTP result was 200, c) there is an existing + # download already, and d) [if the there was a Content-Length + # returned by the server] that the file sizes match. The logic is + # reversed with a 'not' in step d) to return True if the sizes + # match or there is no Content-Length. + + # This is a test for an edge case where curl does not download + # body content even if the server returned a 200 response. This + # happens when curl is given the 'time-cond' option (which sends + # the HTTP header If-Modified-Since to the server) and the server + # responds with a 200 response but curl terminates the connection + # before any body content is transferred. I.e. curl goes above + # and beyond sending an If-Modified-Since and actually compares + # the Last-Modified header returned to it itself to make a + # decision whether to download the document body. + + # See curl issue report here: + # https://sourceforge.net/p/curl/bugs/806/ + reposadocommon.print_stderr( + 'WARNING: No body provided; assuming already downloaded for %s', + destinationpath) + return header else: # there was a download error of some sort; clean all relevant # downloads that may be in a bad state. - for f in [tempdownloadpath, destinationpath]: + for filename in [tempdownloadpath, destinationpath]: try: - os.unlink(f) + os.unlink(filename) except OSError: pass raise HTTPError(http_result, - header.get('http_result_description','')) + header.get('http_result_description', '')) def getURL(url, destination_path): @@ -431,8 +461,8 @@ def getURL(url, destination_path): else: saved_etag = None try: - header = curl(url, destination_path, - onlyifnewer=True, etag=saved_etag) + header = curl(url, destination_path, + onlyifnewer=True, etag=saved_etag) except CurlError, err: err = 'Error %s: %s' % tuple(err) raise CurlDownloadError(err) @@ -457,7 +487,7 @@ def getURL(url, destination_path): os.utime(destination_path, (time.time(), modtimeint)) if header.get('etag'): # store etag for future use - record_etag(url, header['etag']) + record_etag(url, header['etag']) _ETAG = {} @@ -483,24 +513,24 @@ def writeEtagDict(): reposadocommon.writeDataToPlist(_ETAG, 'ETags.plist') -class ReplicationError (Exception): +class ReplicationError(Exception): '''A custom error when replication fails''' pass -def replicateURLtoFilesystem(full_url, root_dir=None, +def replicateURLtoFilesystem(full_url, root_dir=None, base_url=None, copy_only_if_missing=False, appendToFilename=''): - '''Downloads a URL and stores it in the same relative path on our + '''Downloads a URL and stores it in the same relative path on our filesystem. Returns a path to the replicated file.''' - + if root_dir == None: root_dir = reposadocommon.pref('UpdatesRootDir') - + if base_url: if not full_url.startswith(base_url): - raise ReplicationError('%s is not a resource in %s' % - (full_url, base_url)) + raise ReplicationError('%s is not a resource in %s' + % (full_url, base_url)) relative_url = full_url[len(base_url):].lstrip('/') else: (unused_scheme, unused_netloc, @@ -521,15 +551,15 @@ def replicateURLtoFilesystem(full_url, root_dir=None, except CurlDownloadError, err: raise ReplicationError(err) return local_file_path - -class ArchiveError (Exception): + +class ArchiveError(Exception): '''A custom error when archiving fails''' pass def archiveCatalog(catalogpath): - '''Makes a copy of our catalog in our archive folder, + '''Makes a copy of our catalog in our archive folder, marking with a date''' archivedir = os.path.join(os.path.dirname(catalogpath), 'archive') if not os.path.exists(archivedir): @@ -566,18 +596,18 @@ def getPreferredLocalization(list_of_localizations): from Foundation import NSBundle except ImportError: # Foundation NSBundle isn't available, use prefs instead - languages = (reposadocommon.pref('PreferredLocalizations') - or ['English', 'en']) + languages = (reposadocommon.pref('PreferredLocalizations') + or ['English', 'en']) for language in languages: if language in list_of_localizations: return language else: - preferred_langs = \ + preferred_langs = ( NSBundle.preferredLocalizationsFromArray_forPreferences_( - list_of_localizations, None) + list_of_localizations, None)) if preferred_langs: return preferred_langs[0] - + if 'English' in list_of_localizations: return 'English' elif 'en' in list_of_localizations: @@ -607,20 +637,20 @@ def sync(fast_scan=False, download_packages=True): reposadocommon.print_stdout('repo_sync run started') catalog_urls = reposadocommon.pref('AppleCatalogURLs') products = reposadocommon.getProductInfo() - + # clear cached AppleCatalog listings for item in products.keys(): products[item]['AppleCatalogs'] = [] replicated_products = [] - + for catalog_url in catalog_urls: - localcatalogpath = \ - reposadocommon.getLocalPathNameFromURL(catalog_url) + '.apple' + localcatalogpath = ( + reposadocommon.getLocalPathNameFromURL(catalog_url) + '.apple') if os.path.exists(localcatalogpath): archiveCatalog(localcatalogpath) try: - localcatalogpath = replicateURLtoFilesystem(catalog_url, - appendToFilename='.apple') + localcatalogpath = replicateURLtoFilesystem( + catalog_url, appendToFilename='.apple') except ReplicationError, err: reposadocommon.print_stderr( 'Could not replicate %s: %s', catalog_url, err) @@ -634,7 +664,7 @@ def sync(fast_scan=False, download_packages=True): if 'Products' in catalog: product_keys = list(catalog['Products'].keys()) reposadocommon.print_stdout('%s products found in %s', - len(product_keys), catalog_url) + len(product_keys), catalog_url) product_keys.sort() for product_key in product_keys: if product_key in replicated_products: @@ -649,14 +679,14 @@ def sync(fast_scan=False, download_packages=True): if download_packages and 'ServerMetadataURL' in product: try: unused_path = replicateURLtoFilesystem( - product['ServerMetadataURL'], + product['ServerMetadataURL'], copy_only_if_missing=fast_scan) except ReplicationError, err: reposadocommon.print_stderr( 'Could not replicate %s: %s', product['ServerMetadataURL'], err) continue - + if download_packages: for package in product.get('Packages', []): # TO-DO: Check 'Size' attribute and make sure @@ -665,7 +695,7 @@ def sync(fast_scan=False, download_packages=True): if 'URL' in package: try: unused_path = replicateURLtoFilesystem( - package['URL'], + package['URL'], copy_only_if_missing=fast_scan) except ReplicationError, err: reposadocommon.print_stderr( @@ -675,40 +705,40 @@ def sync(fast_scan=False, download_packages=True): if 'MetadataURL' in package: try: unused_path = replicateURLtoFilesystem( - package['MetadataURL'], + package['MetadataURL'], copy_only_if_missing=fast_scan) except ReplicationError, err: reposadocommon.print_stderr( 'Could not replicate %s: %s', package['MetadataURL'], err) continue - + # calculate total size size = 0 for package in product.get('Packages', []): size += package.get('Size', 0) - + distributions = product['Distributions'] preferred_lang = getPreferredLocalization( distributions.keys()) preferred_dist = None - + for dist_lang in distributions.keys(): dist_url = distributions[dist_lang] - if (download_packages or - dist_lang == preferred_lang): + if (download_packages or + dist_lang == preferred_lang): try: dist_path = replicateURLtoFilesystem( - dist_url, + dist_url, copy_only_if_missing=fast_scan) if dist_lang == preferred_lang: preferred_dist = dist_path except ReplicationError, err: reposadocommon.print_stderr( 'Could not replicate %s: %s', dist_url, err) - + if not preferred_dist: - # we didn't download the .dist for the preferred + # we didn't download the .dist for the preferred # language. Let's use English. if 'English' in distributions.keys(): dist_lang = 'English' @@ -721,8 +751,8 @@ def sync(fast_scan=False, download_packages=True): continue dist_url = distributions[dist_lang] preferred_dist = reposadocommon.getLocalPathNameFromURL( - dist_url) - + dist_url) + dist = parseSUdist(preferred_dist) if not dist: reposadocommon.print_stderr( @@ -732,36 +762,33 @@ def sync(fast_scan=False, download_packages=True): products[product_key]['title'] = dist['title'] products[product_key]['version'] = dist['version'] products[product_key]['size'] = str(size) - products[product_key]['description'] = \ - dist['description'] - products[product_key]['PostDate'] = \ - product['PostDate'] - products[product_key]['pkg_refs'] = \ - dist['pkg_refs'] - + products[product_key]['description'] = dist['description'] + products[product_key]['PostDate'] = product['PostDate'] + products[product_key]['pkg_refs'] = dist['pkg_refs'] + # if we got this far, we've replicated the product data replicated_products.append(product_key) - + # record original catalogs in case the product is # deprecated in the future #if not 'OriginalAppleCatalogs' in products[product_key]: # products[product_key]['OriginalAppleCatalogs'] = \ # products[product_key]['AppleCatalogs'] - - # If AppleCatalogs list is non-empty, record to + + # If AppleCatalogs list is non-empty, record to # OriginalAppleCatalogs in case the product is deprecated # in the future # # (This is a change from the original implementation to # account for products being mistakenly released for the - # wrong sucatalogs and later corrected. The assumption now - # is that a change in available catalogs means Apple is + # wrong sucatalogs and later corrected. The assumption now + # is that a change in available catalogs means Apple is # fixing a mistake; disappearing from all catalogs means # an item is deprecated.) if products[product_key]['AppleCatalogs']: - products[product_key]['OriginalAppleCatalogs'] = \ - products[product_key]['AppleCatalogs'] - + products[product_key]['OriginalAppleCatalogs'] = ( + products[product_key]['AppleCatalogs']) + # record products we've successfully downloaded reposadocommon.writeDownloadStatus(replicated_products) # write our ETags to disk for future use @@ -770,25 +797,26 @@ def sync(fast_scan=False, download_packages=True): reposadocommon.writeProductInfo(products) # write our local (filtered) catalogs reposadocommon.writeLocalCatalogs(localcatalogpath) - + # clean up tmpdir cleanUpTmpDir() reposadocommon.print_stdout('repo_sync run ended') - + def main(): '''Main command processing''' parser = optparse.OptionParser() parser.set_usage('''Usage: %prog [options]''') - parser.add_option('--recheck', action='store_true', - help="""Recheck already downloaded packages for changes.""") parser.add_option('--log', dest='logfile', metavar='LOGFILE', - help="""Log all output to LOGFILE. No output to STDOUT.""") + help='Log all output to LOGFILE. No output to STDOUT.') + parser.add_option('--recheck', action='store_true', + help='Recheck already downloaded packages for changes.') + options, unused_arguments = parser.parse_args() if reposadocommon.validPreferences(): if not os.path.exists(reposadocommon.pref('CurlPath')): reposadocommon.print_stderr('ERROR: curl tool not found at %s', - reposadocommon.pref('CurlPath')) + reposadocommon.pref('CurlPath')) exit(-1) if not reposadocommon.pref('LocalCatalogURLBase'): download_packages = False @@ -798,11 +826,10 @@ def main(): reposadocommon.LOGFILE = options.logfile elif reposadocommon.pref('RepoSyncLogFile'): reposadocommon.LOGFILE = reposadocommon.pref('RepoSyncLogFile') - - sync(fast_scan=(not options.recheck), + + sync(fast_scan=(not options.recheck), download_packages=download_packages) if __name__ == '__main__': main() - diff --git a/SUS/var/lib/reposado/reposadolib/__init__.py b/SUS/var/lib/reposado/reposadolib/__init__.py old mode 100755 new mode 100644 diff --git a/SUS/var/lib/reposado/reposadolib/reposadocommon.py b/SUS/var/lib/reposado/reposadolib/reposadocommon.py index 114140f..3643c19 100755 --- a/SUS/var/lib/reposado/reposadolib/reposadocommon.py +++ b/SUS/var/lib/reposado/reposadolib/reposadocommon.py @@ -47,6 +47,7 @@ import urlparse import warnings from xml.parsers.expat import ExpatError +from xml.dom import minidom def get_main_dir(): '''Returns the directory name of the script or the directory name of the exe @@ -85,9 +86,15 @@ def pref(prefname): ('https://swscan.apple.com/content/catalogs/others/' 'index-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1' '.sucatalog'), - ('https://swscan.apple.com/content/catalogs/others/' - 'index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard' - '.merged-1.sucatalog') + ('https://swscan.apple.com/content/catalogs/others/' + 'index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard' + '.merged-1.sucatalog'), + ('https://swscan.apple.com/content/catalogs/others/' + 'index-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-' + 'leopard.merged-1.sucatalog'), + ('https://swscan.apple.com/content/catalogs/others/' + 'index-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-' + 'leopard.merged-1.sucatalog'), ], 'PreferredLocalizations': ['English', 'en'], 'CurlPath': '/usr/bin/curl' @@ -229,6 +236,20 @@ def print_stderr(msg, *args): print >> sys.stderr, concat_message(msg, *args) +def humanReadable(size_in_bytes): + """Returns sizes in human-readable units.""" + try: + size_in_bytes = int(size_in_bytes) + except ValueError: + size_in_bytes = 0 + units = [(" KB", 10**6), (" MB", 10**9), (" GB", 10**12), (" TB", 10**15)] + for suffix, limit in units: + if size_in_bytes > limit: + continue + else: + return str(round(size_in_bytes/float(limit/2**10), 1)) + suffix + + def writeDataToPlist(data, filename): '''Writes a dict or list to a plist in our metadata dir''' metadata_dir = pref('UpdatesMetadataDir') @@ -416,7 +437,7 @@ def writeBranchCatalogs(localcatalogpath): catalog['Products'][product_key] = catalog_entry continue else: - if pref('LocalCatalogURLBase') : + if pref('LocalCatalogURLBase'): print_stderr( 'WARNING: Product %s not added to branch %s of %s. ' 'It is not in the corresponding Apple catalogs ' @@ -475,6 +496,88 @@ def writeLocalCatalogs(applecatalogpath): # now write filtered catalogs (branches) based on this catalog writeBranchCatalogs(localcatalogpath) + +def readXMLfile(filename): + '''Return dom from XML file or None''' + try: + dom = minidom.parse(filename) + except ExpatError: + print_stderr( + 'Invalid XML in %s', filename) + return None + except IOError, err: + print_stderr( + 'Error reading %s: %s', filename, err) + return None + return dom + + +def writeXMLtoFile(node, path): + '''Write XML dom node to file''' + xml_string = node.toxml('utf-8') + try: + fileobject = open(path, mode='w') + print >> fileobject, xml_string + fileobject.close() + except (OSError, IOError): + print_stderr('Couldn\'t write XML to %s' % path) + + +def remove_config_data_attribute(product_list): + '''Wrapper to emulate previous behavior of remove-only only operation.''' + check_or_remove_config_data_attribute(product_list, remove_attr=True) + + +def check_or_remove_config_data_attribute( + product_list, remove_attr=False, products=None, suppress_output=False): + '''Loop through the type="config-data" attributes from the distribution + options for a list of products. Return a list of products that have + this attribute set or if `remove_attr` is specified then remove the + attribute from the distribution file. + + This makes softwareupdate find and display updates like + XProtectPlistConfigData and Gatekeeper Configuration Data, which it + normally does not.''' + if not products: + products = getProductInfo() + config_data_products = set() + for key in product_list: + if key in products: + if products[key].get('CatalogEntry'): + distributions = products[key]['CatalogEntry'].get( + 'Distributions', {}) + for lang in distributions.keys(): + distPath = getLocalPathNameFromURL( + products[key]['CatalogEntry']['Distributions'][lang]) + if not os.path.exists(distPath): + continue + dom = readXMLfile(distPath) + if dom: + found_config_data = False + option_elements = ( + dom.getElementsByTagName('options') or []) + for element in option_elements: + if 'type' in element.attributes.keys(): + if (element.attributes['type'].value + == 'config-data'): + found_config_data = True + config_data_products.add(key) + if remove_attr: + element.removeAttribute('type') + # done editing dom + if found_config_data and remove_attr: + try: + writeXMLtoFile(dom, distPath) + except (OSError, IOError): + pass + else: + if not suppress_output: + print_stdout('Updated dist: %s', distPath) + elif not found_config_data: + if not suppress_output: + print_stdout('No config-data in %s', distPath) + return list(config_data_products) + LOGFILE = None def main(): '''Placeholder''' diff --git a/SUS/var/lib/reposado/repoutil b/SUS/var/lib/reposado/repoutil index 987b4e5..f1040b5 100755 --- a/SUS/var/lib/reposado/repoutil +++ b/SUS/var/lib/reposado/repoutil @@ -33,7 +33,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -'''A tool to replicate most of the functionality of +'''A tool to replicate most of the functionality of Apple Software Update server''' import optparse @@ -61,9 +61,6 @@ def getProductLocation(product, product_id): '''Returns local path to replicated product We pass in the product dictionary to avoid calling reposadocommon.getProductInfo(), which is slow.''' - if not reposadocommon.pref('LocalCatalogURLBase'): - # we're not replicating products - return None if not 'CatalogEntry' in product: # something is wrong with the product entry return None @@ -94,7 +91,7 @@ def getRestartNeeded(product): '''Returns "Yes" if all pkg_refs require a restart or shutdown, "No" if none do, and "Sometimes" if some do and some don't. Returns "UNKNOWN" if there is no pkg_ref data for the update.''' - + pkgs = product.get('pkg_refs', {}).keys() pkg_count = len(pkgs) if pkg_count == 0: @@ -127,16 +124,16 @@ def print_info(key): catalog_branches = reposadocommon.getCatalogBranches() branchlist = [branch for branch in catalog_branches.keys() if key in catalog_branches[branch]] - + reposadocommon.print_stdout('Product: %s', key) reposadocommon.print_stdout('Title: %s', product.get('title')) reposadocommon.print_stdout('Version: %s', product.get('version')) - reposadocommon.print_stdout('Size: %s', - humanReadable(product.get('size', 0))) + reposadocommon.print_stdout('Size: %s', + reposadocommon.humanReadable(product.get('size', 0))) reposadocommon.print_stdout( 'Post Date: %s', product.get('PostDate')) - reposadocommon.print_stdout('RestartNeeded: %s', - getRestartNeeded(product)) + reposadocommon.print_stdout( + 'RestartNeeded: %s', getRestartNeeded(product)) if reposadocommon.pref('LocalCatalogURLBase'): # we're replicating products locally reposadocommon.print_stdout('Status: %s', status) @@ -176,7 +173,8 @@ def print_dist(key): for lang in languages: if products[key]['CatalogEntry']['Distributions'].get(lang): distPath = reposadocommon.getLocalPathNameFromURL( - products[key]['CatalogEntry']['Distributions'][lang]) + products[key]['CatalogEntry'][ + 'Distributions'][lang]) try: distFd = open(distPath, 'r') distContents = distFd.read() @@ -197,20 +195,6 @@ def list_branches(): reposadocommon.print_stdout(key) -def humanReadable(size_in_bytes): - """Returns sizes in human-readable units.""" - try: - size_in_bytes = int(size_in_bytes) - except ValueError: - size_in_bytes = 0 - units = [(" KB", 2**20), (" MB", 2**30), (" GB", 2**40), (" TB", 2**50)] - for suffix, limit in units: - if size_in_bytes > limit: - continue - else: - return str(round(size_in_bytes/float(limit/2**10), 1)) + suffix - - def print_product_line(key, products, catalog_branches=None): '''Prints a line of product info''' if key in products: @@ -226,15 +210,15 @@ def print_product_line(key, products, catalog_branches=None): deprecation_state = '(Deprecated)' try: post_date = products[key].get('PostDate').strftime('%Y-%m-%d') - except Exception: + except BaseException: post_date = 'None' reposadocommon.print_stdout( '%-15s %-50s %-10s %-10s %s %s', - key, - products[key].get('title'), + key, + products[key].get('title'), products[key].get('version'), post_date, - branchlist, + branchlist, deprecation_state) else: reposadocommon.print_stdout('%-15s ', key) @@ -286,6 +270,24 @@ def list_deprecated(sort_order='date', reverse_sort=False): list_products(sort_order, reverse_sort, list_of_productids) +def list_non_deprecated(sort_order='date', reverse_sort=False): + '''Find products that are referenced in Apple\'s catalogs''' + products = reposadocommon.getProductInfo() + list_of_productids = [key for key in products.keys() + if products[key].get('AppleCatalogs')] + list_products(sort_order, reverse_sort, list_of_productids) + + +def list_config_data(sort_order='date', reverse_sort=False): + '''Find updates with \'type="config-data"\' attribute''' + product_info = reposadocommon.getProductInfo() + product_list = product_info.keys() + matching_products = reposadocommon.check_or_remove_config_data_attribute( + product_list, remove_attr=False, products=product_info, + suppress_output=True) + list_products(sort_order, reverse_sort, matching_products) + + def list_products(sort_order='date', reverse_sort=False, list_of_productids=None): '''Prints a list of Software Update products''' @@ -332,34 +334,38 @@ def list_products(sort_order='date', reverse_sort=False, print_product_line(product['key'], products, catalog_branches) for error in errormessages: reposadocommon.print_stderr('WARNING: %s' % error) - - + + def add_product_to_branch(parameters): '''Adds one or more products to a branch. Takes a list of strings. - The last string must be the name of a branch catalog. All other + The last string must be the name of a branch catalog. All other strings must be product_ids.''' # sanity checking for item in parameters: if item.startswith('-'): - reposadocommon.print_stderr('Ambiguous parameters: can\'t tell if ' - '%s is a parameter or an option!', item) + reposadocommon.print_stderr( + 'Ambiguous parameters: can\'t tell if ' + '%s is a parameter or an option!', item) return branch_name = parameters[-1] product_id_list = parameters[0:-1] - + # remove all duplicate product ids product_id_list = list(set(product_id_list)) - + catalog_branches = reposadocommon.getCatalogBranches() if not branch_name in catalog_branches: reposadocommon.print_stderr('Catalog branch %s doesn\'t exist!', - branch_name) + branch_name) return - + products = reposadocommon.getProductInfo() if 'all' in product_id_list: product_id_list = products.keys() - + elif 'non-deprecated' in product_id_list: + product_id_list = [key for key in products.keys() + if products[key].get('AppleCatalogs')] + for product_id in product_id_list: if not product_id in products: reposadocommon.print_stderr( @@ -378,43 +384,48 @@ def add_product_to_branch(parameters): continue if product_id in catalog_branches[branch_name]: reposadocommon.print_stderr( - '%s (%s-%s) is already in branch %s!', + '%s (%s-%s) is already in branch %s!', product_id, title, vers, branch_name) else: reposadocommon.print_stdout( - 'Adding %s (%s-%s) to branch %s...', + 'Adding %s (%s-%s) to branch %s...', product_id, title, vers, branch_name) catalog_branches[branch_name].append(product_id) - + reposadocommon.writeCatalogBranches(catalog_branches) reposadocommon.writeAllBranchCatalogs() def remove_product_from_branch(parameters): '''Removes one or more products from a branch. Takes a list of strings. - The last string must be the name of a branch catalog. All other + The last string must be the name of a branch catalog. All other strings must be product_ids.''' - - # sanity checking + + # sanity checking for item in parameters: if item.startswith('-'): reposadocommon.print_stderr( 'Ambiguous parameters: can\'t tell if ' '%s is a parameter or an option!', item) return - + branch_name = parameters[-1] product_id_list = parameters[0:-1] - - # remove all duplicate product ids - product_id_list = list(set(product_id_list)) - + catalog_branches = reposadocommon.getCatalogBranches() if not branch_name in catalog_branches: reposadocommon.print_stderr( 'Catalog branch %s doesn\'t exist!', branch_name) return + products = reposadocommon.getProductInfo() + if 'deprecated' in product_id_list: + product_id_list = [key for key in catalog_branches[branch_name] + if not products[key].get('AppleCatalogs')] + else: + # remove all duplicate product ids + product_id_list = list(set(product_id_list)) + for product_id in product_id_list: if product_id in products: title = products[product_id].get('title') @@ -425,45 +436,45 @@ def remove_product_from_branch(parameters): title = 'UNKNOWN' vers = 'UNKNOWN' if not product_id in catalog_branches[branch_name]: - reposadocommon.print_stderr('%s (%s-%s) is not in branch %s!', - product_id, title, vers, branch_name) + reposadocommon.print_stderr('%s (%s-%s) is not in branch %s!', + product_id, title, vers, branch_name) continue - reposadocommon.print_stdout('Removing %s (%s-%s) from branch %s...', - product_id, title, vers, branch_name) + reposadocommon.print_stdout('Removing %s (%s-%s) from branch %s...', + product_id, title, vers, branch_name) catalog_branches[branch_name].remove(product_id) reposadocommon.writeCatalogBranches(catalog_branches) reposadocommon.writeAllBranchCatalogs() def purge_product(product_ids, force=False): - '''Removes products from the ProductInfo.plist and purges their local + '''Removes products from the ProductInfo.plist and purges their local replicas (if they exist). Warns and skips if a product is not deprecated - or is in any branch, unless force == True. If force == True, product is + or is in any branch, unless force == True. If force == True, product is also removed from all branches. This action is destructive and cannot be undone. product_ids is a list of productids.''' - + # sanity checking for item in product_ids: if item.startswith('-'): reposadocommon.print_stderr('Ambiguous parameters: can\'t tell if ' - '%s is a parameter or an option!', item) + '%s is a parameter or an option!', item) return - + products = reposadocommon.getProductInfo() catalog_branches = reposadocommon.getCatalogBranches() downloaded_product_list = reposadocommon.getDownloadStatus() - + if 'all-deprecated' in product_ids: product_ids.remove('all-deprecated') deprecated_productids = [key for key in products.keys() if not products[key].get('AppleCatalogs')] product_ids.extend(deprecated_productids) - + # remove all duplicate product ids product_ids = list(set(product_ids)) - + for product_id in product_ids: if not product_id in products: reposadocommon.print_stderr( @@ -471,7 +482,8 @@ def purge_product(product_ids, force=False): 'Skipping.', product_id) continue product = products[product_id] - product_short_info = ('%s (%s-%s)' + product_short_info = ( + '%s (%s-%s)' % (product_id, product.get('title'), product.get('version'))) if product.get('AppleCatalogs') and not force: reposadocommon.print_stderr( @@ -492,15 +504,15 @@ def purge_product(product_ids, force=False): # remove product from all branches for branch_name in branches_with_product: reposadocommon.print_stdout( - 'Removing %s from branch %s...', + 'Removing %s from branch %s...', product_short_info, branch_name) catalog_branches[branch_name].remove(product_id) - + local_copy = getProductLocation(product, product_id) if local_copy: # remove local replica reposadocommon.print_stdout( - 'Removing replicated %s from %s...', + 'Removing replicated %s from %s...', product_short_info, local_copy) try: shutil.rmtree(local_copy) @@ -513,7 +525,7 @@ def purge_product(product_ids, force=False): # delete product from downloaded product list if product_id in downloaded_product_list: downloaded_product_list.remove(product_id) - + # write out changed catalog branches, productInfo, # and rebuild our local and branch catalogs reposadocommon.writeDownloadStatus(downloaded_product_list) @@ -522,9 +534,9 @@ def purge_product(product_ids, force=False): reposadocommon.writeAllLocalCatalogs() -def copy_branches(source_branch, dest_branch): +def copy_branches(source_branch, dest_branch, force=False): '''Copies source_branch to dest_branch, replacing dest_branch''' - # sanity checking + # sanity checking for branch in [source_branch, dest_branch]: if branch.startswith('-'): reposadocommon.print_stderr( @@ -535,31 +547,33 @@ def copy_branches(source_branch, dest_branch): if not source_branch in catalog_branches: reposadocommon.print_stderr('Branch %s does not exist!', source_branch) return - if dest_branch in catalog_branches: + if dest_branch in catalog_branches and not force: answer = raw_input( - 'Really replace contents of branch %s with branch %s? [y/n] ' - % (dest_branch, source_branch)) + 'Really replace contents of branch %s with branch %s? [y/n] ' + % (dest_branch, source_branch)) if not answer.lower().startswith('y'): return catalog_branches[dest_branch] = catalog_branches[source_branch] reposadocommon.print_stdout('Copied contents of branch %s to branch %s.', - source_branch, dest_branch) + source_branch, dest_branch) reposadocommon.writeCatalogBranches(catalog_branches) reposadocommon.writeAllBranchCatalogs() - - -def delete_branch(branchname): + + +def delete_branch(branchname, force=False): '''Deletes a branch''' catalog_branches = reposadocommon.getCatalogBranches() if not branchname in catalog_branches: reposadocommon.print_stderr('Branch %s does not exist!', branchname) return - answer = raw_input('Really remove branch %s? [y/n] ' % branchname) - if answer.lower().startswith('y'): - del catalog_branches[branchname] - deleteBranchCatalogs(branchname) - reposadocommon.writeCatalogBranches(catalog_branches) - + if not force: + answer = raw_input('Really remove branch %s? [y/n] ' % branchname) + if not answer.lower().startswith('y'): + return + del catalog_branches[branchname] + deleteBranchCatalogs(branchname) + reposadocommon.writeCatalogBranches(catalog_branches) + def new_branch(branchname): '''Creates a new empty branch''' @@ -576,82 +590,123 @@ def configure(): reposadocommon.configure_prefs() +def remove_config_data(product_ids): + '''Remove the config-data attribute from product dist files''' + if len(product_ids) == 1 and product_ids[0] == 'all': + '''Removes the config-data attribute from all products''' + reposadocommon.print_stdout( + 'Checking all products for config-data attributes...') + product_info = reposadocommon.getProductInfo() + product_list = product_info.keys() + updated_products = reposadocommon.check_or_remove_config_data_attribute( + product_list, remove_attr=True, products=product_info, + suppress_output=True) + if updated_products: + reposadocommon.print_stdout( + 'config-data attribute removed from:') + for key in updated_products: + reposadocommon.print_stdout( + ' %s: %s-%s', + key, product_info[key]['title'], product_info[key]['version']) + else: + reposadocommon.print_stdout( + 'No products with config-data attributes found.') + else: + reposadocommon.remove_config_data_attribute(product_ids) + + def main(): '''Main command processing''' - + p = optparse.OptionParser() p.set_usage('''Usage: %prog [options]''') #p.add_option('--sync', action='store_true', - # help="""Synchronize Apple updates""") + # help="""Synchronize Apple updates""") p.add_option('--configure', action='store_true', - help="""Configure Reposado preferences.""") + help='Configure Reposado preferences.') p.add_option('--products', '--updates', action='store_true', - dest='products', - help="""List available updates""") + dest='products', + help='List available updates.') p.add_option('--deprecated', action='store_true', - help="""List deprecated updates""") + help='List deprecated updates.') + p.add_option('--non-deprecated', action='store_true', + help='List non-deprecated updates.') + p.add_option('--config-data', action='store_true', + help="""List updates with 'type="config-data"' attribute""") p.add_option('--sort', metavar='SORT_ORDER', default='date', - help="""Sort list. - Available sort orders are: date, title, id""") + help='Sort list.\n' + 'Available sort orders are: date, title, id.') p.add_option('--reverse', action='store_true', - help="""Reverse sort order.""") - p.add_option('--branches', '--catalogs', - dest='list_branches', action='store_true', - help="""List available branch catalogs""") + help='Reverse sort order.') + p.add_option('--branches', '--catalogs', + dest='list_branches', action='store_true', + help='List available branch catalogs.') p.add_option('--new-branch', - metavar='BRANCH_NAME', - help='''Create new empty branch BRANCH_NAME.''') + metavar='BRANCH_NAME', + help='Create new empty branch BRANCH_NAME.') p.add_option('--delete-branch', - metavar='BRANCH_NAME', - help='''Delete branch BRANCH_NAME.''') + metavar='BRANCH_NAME [--force]', + help='Delete branch BRANCH_NAME.') p.add_option('--copy-branch', nargs=2, - metavar='SOURCE_BRANCH DEST_BRANCH', - help='''Copy all items from SOURCE_BRANCH to - DEST_BRANCH. If DEST_BRANCH does not exist, - it will be created.''') - p.add_option('--list-branch', '--list-catalog', - dest='branch', - metavar='BRANCH_NAME', - help="""List updates in branch BRANCH_NAME""") - p.add_option('--diff', '--diff-branch', '--diff-branches', - dest='diff_branch', nargs=2, - metavar='BRANCH1_NAME BRANCH2_NAME', - help="""Display differences between two branches""") + metavar='SOURCE_BRANCH DEST_BRANCH [--force]', + help='Copy all items from SOURCE_BRANCH to ' + 'DEST_BRANCH. If DEST_BRANCH does not exist, ' + 'it will be created.') + p.add_option('--list-branch', '--list-catalog', + dest='branch', + metavar='BRANCH_NAME', + help='List updates in branch BRANCH_NAME.') + p.add_option('--diff', '--diff-branch', '--diff-branches', + dest='diff_branch', nargs=2, + metavar='BRANCH1_NAME BRANCH2_NAME', + help='Display differences between two branches.') p.add_option('--product-info', '--info', metavar='PRODUCT_ID', - dest='info', - help="""Print info on a specific update.""") + dest='info', + help='Print info on a specific update.') p.add_option('--product-dist', '--dist', metavar='PRODUCT_ID', - dest='dist', - help="""Print the contents of the .dist file for a specific - update.""") + dest='dist', + help='Print the contents of the .dist file for a specific ' + 'update.') p.add_option('--add-product', '--add-products', '--add-update', '--add-updates', '--add', - dest='add_product', nargs=2, - metavar='PRODUCT_ID [PRODUCT_ID ...] BRANCH_NAME', - help='''Add one or more PRODUCT_IDs to catalog branch - BRANCH_NAME. --add-product all BRANCH_NAME will add - all cached products, including deprecated products, to - catalog BRANCH_NAME.''') + dest='add_product', nargs=2, + metavar='PRODUCT_ID [PRODUCT_ID ...] BRANCH_NAME', + help='Add one or more PRODUCT_IDs to catalog branch ' + 'BRANCH_NAME. --add-product all BRANCH_NAME will add ' + 'all cached products, including deprecated products, to ' + 'catalog BRANCH_NAME. --add-product non-deprecated ' + 'BRANCH_NAME will add all non-deprecated products to ' + 'catalog BRANCH_NAME.') p.add_option('--remove-product', '--remove-products', nargs=2, - metavar='PRODUCT_ID [PRODUCT_ID ...] BRANCH_NAME', - help='''Remove one or more PRODUCT_IDs from catalog branch - BRANCH_NAME.''') + metavar='PRODUCT_ID [PRODUCT_ID ...] BRANCH_NAME', + help='Remove one or more PRODUCT_IDs from catalog branch ' + 'BRANCH_NAME. --remove-product deprecated will remove ' + 'all deprecated products from BRANCH_NAME.') + p.add_option('--remove-config-data', + metavar='PRODUCT_ID [PRODUCT_ID ...]', + help='Remove the \'type="config-data"\' attribute from one or ' + 'more PRODUCT_IDs.') p.add_option('--purge-product', '--purge-products', - metavar='PRODUCT_ID [PRODUCT_ID ...] [--force]', - help='''Purge one or more PRODUCT_IDs from product - database and remove any locally replicated version.''') + metavar='PRODUCT_ID [PRODUCT_ID ...] [--force]', + help='Purge one or more PRODUCT_IDs from product ' + 'database and remove any locally replicated version.') p.add_option('--force', action='store_true', - help="""Force purge of product. Must be used with - --purge-product option.""") - - options, arguments = p.parse_args() - + help='Force purge of product, force copy or force delete a ' + 'branch. Must be used with --purge-product, --copy-branch ' + 'or --delete-branch options.') + + options, arguments = p.parse_args() + if options.configure: configure() if options.products: list_products(sort_order=options.sort, reverse_sort=options.reverse) if options.deprecated: list_deprecated(sort_order=options.sort, reverse_sort=options.reverse) + if options.non_deprecated: + list_non_deprecated(sort_order=options.sort, reverse_sort=options.reverse) + if options.config_data: + list_config_data(sort_order=options.sort, reverse_sort=options.reverse) if options.branch: list_branch(options.branch, sort_order=options.sort, reverse_sort=options.reverse) @@ -664,9 +719,10 @@ def main(): if options.new_branch: new_branch(options.new_branch) if options.copy_branch: - copy_branches(options.copy_branch[0], options.copy_branch[1]) + copy_branches( + options.copy_branch[0], options.copy_branch[1], force=options.force) if options.delete_branch: - delete_branch(options.delete_branch) + delete_branch(options.delete_branch, force=options.force) if options.diff_branch: diff_branches(options.diff_branch) if options.add_product: @@ -681,7 +737,11 @@ def main(): product_ids = [options.purge_product] product_ids.extend(arguments) purge_product(product_ids, force=options.force) - + if options.remove_config_data: + params = [options.remove_config_data] + params.extend(arguments) + remove_config_data(params) + if __name__ == '__main__': main() diff --git a/base/NetSUSInstaller.sh b/base/NetSUSInstaller.sh old mode 100644 new mode 100755 index 86e0db9..2c89d60 --- a/base/NetSUSInstaller.sh +++ b/base/NetSUSInstaller.sh @@ -1,240 +1,199 @@ #!/bin/bash # This script controls the flow of the Linux NetSUS installation -######### Requirements Checking - Root ######### +export PATH="/bin:$PATH" + +netsusdir=/var/appliance + +#==== Check Requirements - Root User ======================# if [[ "$(id -u)" != "0" ]]; then - echo "The NetSUS Installer needs to be run as root or using sudo." - exit 1 + echo "The NetSUS Installer needs to be run as root or using sudo." + exit 1 fi # Needed for systems with secure umask settings -OLD_UMASK=`umask` +OLD_UMASK=$(umask) umask 022 +clean-exit() { + umask "$OLD_UMASK" + exit 0 +} + +clean-fail() { + umask "$OLD_UMASK" + exit 1 +} + # Check for an existing installation if [ -d "/var/appliance" ]; then - upgrade=true + upgrade=true else - upgrade=false + upgrade=false fi # Create NetSUS directory (needed immediately for logging) -if [ ! -d "/var/appliance/logs" ]; then - mkdir -p /var/appliance/logs -fi - -# Logger -source logger.sh +mkdir -p $netsusdir/logs + +source utils/logger.sh + +#==== Parse Arguments =====================================# + +export INTERACTIVE=true + +while getopts "hny" ARG +do + case $ARG in + h) + echo "Usage: $0 [-y]" + echo "-y Activates non-interactive mode, which will silently install the NetSUS without any prompts" + echo "-h Shows this message" + exit 0 + ;; + n) + logCritical "The -n flag is deprecated and will be removed in a future version. + Please use -y instead." + export INTERACTIVE=false + ;; + y) + export INTERACTIVE=false + ;; + esac +done + +#==== Check Requirements ==================================# + +log "Starting the NetSUS Installation" +log "Checking installation requirements..." -######### Requirements Checking ######### - -logEvent "Starting the NetSUS Installation" -logEvent "Checking installation requirements..." +# Check for a 64-bit OS +bash checks/test64bitRequirements.sh || clean-fail failedAnyChecks=0 # Check for Valid OS -. testOSRequirements.sh - -logEvent $detectedOS - -[[ $detectedOS == "Ubuntu" ]] && { bash testUbuntuBinRequirements.sh; } - -# Check for a 64-bit OS -bash test64bitRequirements.sh -if [[ $? -ne 0 ]]; then - failedAnyChecks=1 -fi +bash checks/testOSRequirements.sh || failedAnyChecks=1 +# Check for required binaries +bash checks/testBinRequirements.sh || failedAnyChecks=1 # Abort if we failed any checks if [[ $failedAnyChecks -ne 0 ]]; then - logEvent "Aborting installation due to unsatisfied requirements." - if [[ $FLAGS = "-n" ]]; then - echo "Installation failed. See $logFile for more details." - fi - echo "Installation failed. See $logFile for more details." - umask $OLD_UMASK - exit 1 + log "Aborting installation due to unsatisfied requirements." + if [[ $INTERACTIVE = true ]]; then + # shellcheck disable=SC2154 + echo "Installation failed. See $logFile for more details." + fi + clean-fail fi -logEvent "Passed all requirements checking!" - -######### Verification ######### -# Prompt user for type of installation - echo " -Is this a standalone installation? -Answer yes unless you are creating an image of the appliance to deploy in multiple locations -" - - read -t 1 -n 100000 devnull # This clears any accidental input from stdin - - while [[ $REPLY != [yYnN] ]]; do - read -n1 -p "Standalone? (y/n): " - echo "" - done - standalone=$REPLY +log "Passed all requirements" +#==== Prompt for Confirmation =============================# +if [[ $INTERACTIVE = true ]]; then # Prompt user for permission to continue with the installation - echo " + echo " The following will be installed * Appliance Web Interface +* Software Update Server * NetBoot Server -* Software Updates Server -* LDAP Proxy Server +* LDAP Proxy " + # shellcheck disable=SC2162,SC2034 + read -t 1 -n 100000 devnull # This clears any accidental input from stdin + + while [[ $REPLY != [yYnN] ]]; do + # shellcheck disable=SC2162 + read -n1 -p "Proceed? (y/n): " + echo "" + done + if [[ $REPLY = [nN] ]]; then + log "Aborting..." + clean-exit + else + log "Installing..." + fi +else + log "Installing..." +fi - - read -t 1 -n 100000 devnull # This clears any accidental input from stdin - REPLY="" - while [[ $REPLY != [yYnN] ]]; do - read -n1 -p "Proceed? (y/n): " - echo "" - done - if [[ $REPLY = [nN] ]]; then - logEvent "Aborting..." - umask $OLD_UMASK - exit 0 - else - logEvent "Installing..." - fi - - - -######### Sub-installers ######### - -#Initial Cleanup tasks +#==== Initial Cleanup tasks ===============================# # Set SELinux policy if sestatus | grep -q enforcing ; then - logEvent "Setting SELINUX mode to permissive" - echo "A restart of the system will be required before using the NetSUS" - sed -i "s/SELINUX=enforcing/SELINUX=permissive/" /etc/selinux/config -fi -if [ -f "/selinux/enforce" ]; then - echo 0 > /selinux/enforce - echo + log "Setting SELINUX mode to permissive" + sed -i "s/SELINUX=enforcing/SELINUX=permissive/" /etc/selinux/config + setenforce permissive fi -if [[ $detectedOS == 'Ubuntu' ]]; then - apt-get update -fi - - -# Install Web Interface -bash webadminInstall.run -- $detectedOS -if [[ $? -ne 0 ]]; then - umask $OLD_UMASK - exit 1 -fi +#==== Install Components ==================================# -# Install NetBoot -bash netbootInstall.run -- $detectedOS -if [[ $? -ne 0 ]]; then - umask $OLD_UMASK - exit 1 -fi +bash install-webadmin.sh || clean-fail +bash install-netboot.sh || clean-fail +bash install-sus.sh || clean-fail +bash install-proxy.sh || clean-fail -# Install SUS -bash susInstall.run -- $detectedOS -if [[ $? -ne 0 ]]; then - umask $OLD_UMASK - exit 1 -fi +#==== Post Cleanup tasks ==================================# -# Install LDAP Proxy -bash LDAPProxyInstall.run -- $detectedOS -if [[ $? -ne 0 ]]; then - umask $OLD_UMASK - exit 1 +# Disable IPv6 +if grep -q 'net.ipv6.conf.lo.disable_ipv6' /etc/sysctl.conf; then + sed -i '/Disable IPv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.conf fi +#echo " +## Disable IPv6 +#net.ipv6.conf.all.disable_ipv6 = 1 +#net.ipv6.conf.default.disable_ipv6 = 1 +#" >> /etc/sysctl.conf -#Post Cleanup Tasks -#Disables IPv6 - -echo "# Disable IPv6 -net.ipv6.conf.all.disable_ipv6 = 1 -net.ipv6.conf.default.disable_ipv6 = 1 -net.ipv6.conf.lo.disable_ipv6 = 1" >> /etc/sysctl.conf - - -logEvent "" -logEvent "The NetSUSLP has been installed." +log "" +log "The NetSUSLP has been installed." if [ ! $upgrade = true ]; then - logEvent "Verify that port 443 and 80 are not blocked by a firewall." - logEvent "" - logEvent "Note: IP Helpers are required if using NetBoot across subnets." - logEvent "The NetBoot folder name can not contain any spaces" - logEvent "" -fi - - - -if [ $upgrade = true ]; then - logEvent "If you are upgrading NetSUSLP, you can simply start using it." -else - logEvent "To complete the installation, open a web browser and navigate to https://${HOSTNAME}:443/." -fi - -# Need to check service names for RedHat -case $standalone in -[yY]) -if [[ $detectedOS == 'Ubuntu' ]]; then - echo "Updating Services..." - service apparmor restart - service slapd stop > /dev/null 2>&1 - service networking restart > /dev/null 2>&1 - service apache2 restart > /dev/null 2>&1 - service netatalk stop > /dev/null 2>&1 - service smbd stop > /dev/null 2>&1 - service tftpd-hpa stop > /dev/null 2>&1 - service openbsd-inetd stop > /dev/null 2>&1 - echo manual > /etc/init/slapd.override - echo manual > /etc/init/netatalk.override - echo manual > /etc/init/smbd.override - echo manual > /etc/init/tftpd-hpa.override - echo manual > /etc/init/openbsd-inetd.override - - logEvent "If you are installing NetSUSLP for the first time, please follow the documentation for setup instructions." -fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - service httpd restart - service smb stop - chkconfig tftp off - service xinetd restart - service netatalk stop - chkconfig smb off - chkconfig netatalk off - chkconfig slapd off - service slapd stop -fi - - ;; -[nN]) -if [[ $detectedOS == 'Ubuntu' ]]; then - chmod +x /etc/init.d/applianceFirstRun - #Need to update for RedHat - update-rc.d applianceFirstRun defaults - cp -R ./etc/* /etc/ - rm /etc/udev/rules.d/70-* - rm /etc/resolv.conf - echo "NetSUSLP installation complete." - echo "Type: \"shutdown -P now\" to Shut Down." -fi -if [[ $detectedOS == 'CentOS' ]] || [[ $detectedOS == 'RedHat' ]]; then - rm -rf /etc/ssh/ssh_host_* - rm -rf /etc/udev/rules.d/70-* - sed -i '/HWADDR=/d' /etc/sysconfig/network-scripts/ifcfg-eth0 - find /var/log -type f -delete - rm -f install.log* - echo "NetSUSLP installation complete." - echo "Type: \"poweroff\" to Shut Down." -fi - ;; -esac - -rm -f "$0" -umask $OLD_UMASK -exit 0 + log "Verify that port 443 and 80 are not blocked by a firewall." + log "" + log "Note: IP Helpers are required if using NetBoot across subnets." + log "The NetBoot folder name can not contain any spaces" + log "" +fi + +log "To complete the installation, open a web browser and navigate to https://${HOSTNAME}:443/." + +if [[ $(which update-rc.d 2>&-) != "" ]]; then + service apparmor restart >> $logFile 2>&1 + service apache2 restart >> $logFile 2>&1 + service slapd stop >> $logFile 2>&1 + service netatalk stop >> $logFile 2>&1 + service smbd stop >> $logFile 2>&1 + service tftpd-hpa stop >> $logFile 2>&1 + # service openbsd-inetd stop >> $logFile 2>&1 + update-rc.d slapd disable >> $logFile 2>&1 + update-rc.d netatalk disable >> $logFile 2>&1 + if [[ $(which systemctl 2>&-) != "" ]]; then + update-rc.d smbd disable >> $logFile 2>&1 + update-rc.d tftpd-hpa disable >> $logFile 2>&1 + systemctl disable nfs-server >> $logFile 2>&1 + # systemctl disable openbsd-inetd >> $logFile 2>&1 + service nfs-server stop >> $logFile 2>&1 + else + echo manual > /etc/init/smbd.override + echo manual > /etc/init/tftpd-hpa.override + update-rc.d nfs-kernel-server disable >> $logFile 2>&1 + # update-rc.d openbsd-inetd disable >> $logFile 2>&1 + service nfs-kernel-server stop >> $logFile 2>&1 + fi + log "If you are installing NetSUSLP for the first time, please follow the documentation for setup instructions." +elif [[ $(which chkconfig 2>&-) != "" ]]; then + service httpd restart >> $logFile 2>&1 + chkconfig tftp off >> $logFile 2>&1 + chkconfig nfs off > /dev/null 2>&1 + #if [ -f "/etc/sysconfig/xinetd" ]; then + # service xinetd restart >> $logFile 2>&1 + #fi + log "If you are installing NetSUSLP for the first time, please follow the documentation for setup instructions." +fi + +clean-exit \ No newline at end of file diff --git a/base/test64bitRequirements.sh b/base/test64bitRequirements.sh old mode 100644 new mode 100755 index dade698..8b9c64a --- a/base/test64bitRequirements.sh +++ b/base/test64bitRequirements.sh @@ -1,16 +1,14 @@ -#!/bin/sh +#!/bin/bash -source logger.sh +logNoNewLine "Checking for a 64-bit OS..." -logEventNoNewLine "Checking for a 64-bit OS..." - -archVersion=`uname -m` +archVersion=$(uname -m) if [[ ${archVersion} != 'x86_64' && ${archVersion} != 'ia64' ]]; then - logEvent "Error: Did not detect a 64-bit kernel (Detected $archVersion)." + log "Error: Did not detect a 64-bit kernel (Detected $archVersion)." exit 1 fi -logEvent "OK" +log "OK" -exit 0 \ No newline at end of file +exit 0 diff --git a/base/testOSRequirements.sh b/base/testOSRequirements.sh index 06ac9d3..336ce9f 100755 --- a/base/testOSRequirements.sh +++ b/base/testOSRequirements.sh @@ -1,51 +1,54 @@ -#!/bin/sh +#!/bin/bash -source logger.sh - -unset detectedOS -logEventNoNewLine "Checking for a supported OS..." - -if [ -f "/usr/bin/lsb_release" ]; then - -ubuntuVersion=`lsb_release -s -d` - -case $ubuntuVersion in -*"Ubuntu 14.04"*) -detectedOS="Ubuntu" -export detectedOS -;; -*"Ubuntu 12.04"*) -detectedOS="Ubuntu" -export detectedOS -;; -*"Ubuntu 10.04"*) -detectedOS="Ubuntu" -export detectedOS -;; -esac +logNoNewLine "Checking for a supported OS..." +if [ -f "/etc/os-release" ]; then + source /etc/os-release +elif [ -e "/etc/system-release" ]; then + NAME=$(sed -e 's/ release.*//' /etc/system-release) + PRETTY_NAME=$(sed -e 's/ release//' /etc/system-release) + VERSION_ID=$(sed -e 's/.*release //;s/ .*//' /etc/system-release) +fi +if [[ -z "$NAME" ]]; then + NAME=$(uname -s) fi -if [ -f "/etc/system-release" ] && [ -z "${detectedOS}" ]; then - -case "$(readlink /etc/system-release)" in -"centos-release") - detectedOS="CentOS" - export detectedOS - ;; -"redhat-release") +case $NAME in +"Ubuntu") + if [[ "$VERSION_ID" == "14.04" ]] || [[ "$VERSION_ID" == "16.04" ]] ; then + log "$PRETTY_NAME found" + exit 0 + else + log "Error: $NAME version must be 14.04 or 16.04 (Detected $VERSION_ID)." + exit 1 + fi +;; +"Red Hat Enterprise Linux"*|"CentOS"*) + if [[ "$VERSION_ID" > "6.3" ]] ; then + log "$PRETTY_NAME found" + exit 0 + else + log "Error: $NAME version must be 6.4 or later (Detected $VERSION_ID)." + exit 1 + fi if yum repolist | grep repolist | grep -q ': 0'; then - logEvent "This system is does not have any available repositories." - failedAnyChecks=1 + log "Error: This system is does not have any available repositories." + exit 1 fi - detectedOS="RedHat" - export detectedOS - ;; +;; +*) + release=$(rpm -q --queryformat '%{RELEASE}' rpm | cut -d '.' -f 2) + if [[ $release == "el6" ]] || [[ $release == "el7" ]] ; then + if [[ "$VERSION_ID" > "6.3" ]] ; then + log "$PRETTY_NAME found" + log "Warning: $NAME is a Red Hat Enterprise Linux variant, proceed with caution." + exit 0 + else + log "Error: $NAME version must be 6.4 or later (Detected $VERSION_ID)." + exit 1 + fi + fi + log "Error: Did not detect a valid Ubuntu/Red Hat/CentOS install (Detected $NAME)." + exit 1 +;; esac - -fi - -if [ "${detectedOS}" != 'Ubuntu' ] && [ "${detectedOS}" != 'RedHat' ] && [ "${detectedOS}" != 'CentOS' ]; then - logEvent "Error: Did not detect a valid Ubuntu/RedHat/Cent OS install." - failedAnyChecks=1 -fi diff --git a/base/testUbuntuBinRequirements.sh b/base/testUbuntuBinRequirements.sh old mode 100644 new mode 100755 index b1c21b8..77648c3 --- a/base/testUbuntuBinRequirements.sh +++ b/base/testUbuntuBinRequirements.sh @@ -1,15 +1,29 @@ -#!/bin/sh +#!/bin/bash -source logger.sh +if [[ $(which apt-get 2>&-) != "" ]]; then -logEventNoNewLine "Checking for required Ubuntu binaries" + logNoNewLine "Checking for required Ubuntu binaries..." -# checking for policycoreutils -pcucheck=`dpkg -s policycoreutils | awk '/Package: / {print $2}'` -[[ "${pcucheck}" != "policycoreutils" ]] && { sudo apt-get install policycoreutils; } + # Ensure that the package lists are re-created to avoid installation failure + # rm -rf /var/lib/apt/lists/* + # Update package lists + apt-get -q update >> $logFile + if [[ $? -ne 0 ]]; then + log "Error: Failed to update package index files." + exit 1 + fi -# checking for gawk -pcucheck=`dpkg -s gawk | awk '/Package: / {print $2}'` -[[ "${pcucheck}" != "gawk" ]] && { sudo apt-get install gawk; } + # Checking for policycoreutils + if [[ $(dpkg -s policycoreutils 2>&- | awk '/Status: / {print $NF}') != "installed" ]]; then + apt-get -qq -y install policycoreutils >> $logFile + if [[ $? -ne 0 ]]; then + log "Error: Failed to install policycoreutils." + exit 1 + fi + fi + + log "OK" + +fi exit 0 diff --git a/docs/README.md b/docs/README.md index 2a79fee..3ed59a9 100755 --- a/docs/README.md +++ b/docs/README.md @@ -12,4 +12,4 @@ * GitHub issues are the primary way for communicating about specific proposed changes to this project. -* There is a discussion board located on [JAMF Nation](https://jamfnation.jamfsoftware.com/viewProduct.html?id=180&view=discussions). You are welcome to create an account and contribute to the discussion by asking questions, discussing bugs, or answering questions you may have knowledge about. +* There is a discussion board located on [JAMF Nation](https://www.jamf.com/jamf-nation/third-party-products/180/netboot-sus-appliance?view=discussions). You are welcome to create an account and contribute to the discussion by asking questions, discussing bugs, or answering questions you may have knowledge about. diff --git a/docs/accounts.md b/docs/accounts.md index 22aa6f0..194448f 100755 --- a/docs/accounts.md +++ b/docs/accounts.md @@ -1 +1 @@ -# Accounts The following table lists the default credentials for all accounts associated with the NetBoot/SUS/LP server: Account | Username | Password ------- | -------- | -------- Web Application | webadmin | webadmin Shell (used to administer the NetBoot/SUS/LP server from the command line) | shelluser | shelluser AFP share | afpuser | afpuser SMB user | smbuser | smbuser You can change the usernames and passwords for the web application and shell accounts. You can also change the passwords for the AFP and SMB shares. ## Changing the Web Application or Shell Credentials 1. Log in to the NetBoot/SUS/LP server web application. 2. In the side navigation menu or in the mobile dropdown menu, click **Settings** . 3. In the "NetBoot/SUS/LDAP Proxy Server" section, click **Accounts** . 4. Change the credentials using the fields and tabs provided. 5. Click **Save**. A message displays, reporting the success or failure of the change. ## Changing the Password for the AFP or SMB Share 1. Log in to the NetBoot/SUS/LP server web application. 2. In the top-right corner of the page, click **Settings** . 3. In the "Shares" section, click **AFP** or **SMB** . 4. Enter and verify the new password. 5. Click **Save**. A message displays, reporting the success or failure of the change. \ No newline at end of file +# Accounts The following table lists the default credentials for all accounts associated with the NetBoot/SUS/LP server: Account | Username | Password ------- | -------- | -------- Web Application | webadmin | webadmin Shell (used to administer the NetBoot/SUS/LP server from the command line) | shelluser | shelluser AFP share | afpuser | afpuser1 SMB user | smbuser | smbuser1 You can change the usernames and passwords for the web application and shell accounts. You can also change the passwords for the AFP and SMB shares. ## Changing the Web Application or Shell Credentials 1. Log in to the NetBoot/SUS/LP server web application. 2. In the side navigation menu or in the mobile dropdown menu, click **Settings** . 3. In the "NetBoot/SUS/LDAP Proxy Server" section, click **Accounts** . 4. Change the credentials using the fields and tabs provided. 5. Click **Save**. A message displays, reporting the success or failure of the change. ## Changing the Password for the AFP or SMB Share 1. Log in to the NetBoot/SUS/LP server web application. 2. In the top-right corner of the page, click **Settings** . 3. In the "Shares" section, click **AFP** or **SMB** . 4. Enter and verify the new password. 5. Click **Save**. A message displays, reporting the success or failure of the change. \ No newline at end of file diff --git a/docs/getting_started.md b/docs/getting_started.md index 9b5e16d..2e01281 100755 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -1,25 +1,45 @@ ## Requirements - To install the NetBoot/SUS/LP server using an installer, you need: -* The NetBoot/SUS/LP Server Installer (.run), available at: * One of the following operating systems: * Ubuntu 10.04 LTS Server * Ubuntu 12.04 LTS Server * Ubuntu 14.04 LTS Server * Red Hat Enterprise Linux (RHEL) 6.4 or later - * CentOS 6.4 or later * 300 GB of disk space available +To install the NetBoot/SUS/LP server using an installer, you need: + +* The NetBoot/SUS/LP Server Installer (.run), available at: + +* One of the following operating systems: + * Ubuntu 14.04 LTS Server + * Ubuntu 16.04 LTS Server + * Red Hat Enterprise Linux (RHEL) 6.4 or later + * CentOS 6.4 or later +* 500 GB of disk space available * 1 GB of RAM - To set up the NetBoot/SUS/LP server as an appliance, you need: -* The OVA file for the NetBoot/SUS/LP server, available at: * Virtualization software that supports Open Virtualization Format -* 300 GB of disk space available * 2 GB of RAM - To host a NetBoot server using the NetBoot/SUS/LP server, you need a NetBoot image (.nbi folder). For more information, see the following Knowledge Base article: - [Creating a NetBoot Image and Setting Up a NetBoot Server](https://jamfnation.jamfsoftware.com/article.html?id=307) - **Only Intel-based Macs can use a NetBoot server hosted by the NetBoot/SUS/LP server.** +To set up the NetBoot/SUS/LP server as an appliance, you need: + +* The OVA file for the NetBoot/SUS/LP server, available at: + +* Virtualization software that supports Open Virtualization Format +* 500 GB of disk space available +* 2 GB of RAM + +To host a NetBoot server using the NetBoot/SUS/LP server, you need a NetBoot image (.nbi folder). For more information, see the following Knowledge Base article: + +[Creating a NetBoot Image and Setting Up a NetBoot Server](https://www.jamf.com/jamf-nation/articles/307/creating-a-netboot-image-and-setting-up-a-netboot-server) + +**Only Intel-based Macs can use a NetBoot server hosted by the NetBoot/SUS/LP server.** + +## Installing the NetBoot/SUS/LP Server Using an Installer +1. Copy the NetBoot/SUS/LP Installer (.run) to the server on which you plan to install the NetBoot/SUS /LP server. + +2. Log in to the server as a user with superuser privileges. + +3. Initiate the installer by executing a command similar to the following: + + sudo /path/to/NetSUSLPInstaller.run + +4. Type "y" to proceed. -## Installing the NetBoot/SUS/LP Server Using an Installer 1. Copy the NetBoot/SUS/LP Installer (.run) to the server on which you plan to install the NetBoot/SUS /LP server. - 2. Log in to the server as a user with superuser privileges. - 3. Initiate the installer by executing a command similar to the following: +5. Go to `https://myhostname.local/webadmin` to access the NetBoot/SUS/LP server web application. Once the NetBoot/SUS/LP server is installed, it is recommended that you log in to the web application and change all usernames and passwords associated with the server. For more information, see [Accounts](accounts.md). - sudo /path/to/NetSUSLP_4.0.0.run - 4. When prompted to specify whether the installation is a standalone installation, type "y" unless you are planning to create a package of the NetBoot/SUS/LP server and deploy it to another server. - 5. Type "y" to proceed. - 6. Go to `https://myhostname.local/webadmin` to access the NetBoot/SUS/LP server web application. Once the NetBoot/SUS/LP server is installed, it is recommended that you log in to the web application and change all usernames and passwords associated with the server. For more information, see [Accounts](accounts.md). +## Setting Up the NetBoot/SUS/LP Server as an Appliance +To set up the NetBoot/SUS/LP server as an appliance, import the OVA file for the NetBoot/SUS/LP server into a virtualization software product. This creates an Ubuntu VM with running SMB and AFP shares. The first time you power on the VM, a page displaying the URL for the NetBoot/SUS/LP server web application appears. -## Setting Up the NetBoot/SUS/LP Server as an Appliance To set up the NetBoot/SUS/LP server as an appliance, import the OVA file for the NetBoot/SUS/LP server into a virtualization software product. This creates an Ubuntu VM with running SMB and AFP shares. The first time you power on the VM, a page displaying the URL for the NetBoot/SUS/LP server web application appears. - Once the NetBoot/SUS/LP server is set up as an appliance, it is recommended that you log in to the web application and change all usernames and passwords associated with the server. For more information, see [Accounts](accounts.md). \ No newline at end of file +Once the NetBoot/SUS/LP server is set up as an appliance, it is recommended that you log in to the web application and change all usernames and passwords associated with the server. For more information, see [Accounts](accounts.md). diff --git a/docs/images/attachments/certificates.png b/docs/images/attachments/certificates.png index 848d1c1..d37e80e 100644 Binary files a/docs/images/attachments/certificates.png and b/docs/images/attachments/certificates.png differ diff --git a/docs/images/attachments/netboot.png b/docs/images/attachments/netboot.png index 6ad05a8..d6c753d 100644 Binary files a/docs/images/attachments/netboot.png and b/docs/images/attachments/netboot.png differ diff --git a/docs/images/thumbnails/logs_icon.png b/docs/images/thumbnails/logs_icon.png new file mode 100644 index 0000000..ede9dcb Binary files /dev/null and b/docs/images/thumbnails/logs_icon.png differ diff --git a/docs/images/thumbnails/storage_icon.png b/docs/images/thumbnails/storage_icon.png new file mode 100644 index 0000000..8a72ea8 Binary files /dev/null and b/docs/images/thumbnails/storage_icon.png differ diff --git a/docs/netboot.md b/docs/netboot.md index 3616ca8..b0a5358 100755 --- a/docs/netboot.md +++ b/docs/netboot.md @@ -1 +1 @@ -# Setting Up the NetBoot Server To set up a NetBoot server, you need a NetBoot image (.nbi folder). For more information, see the following Knowledge Base article: [Creating a NetBoot Image and Setting Up a NetBoot Server](https://jamfnation.jamfsoftware.com/article.html?id=307) 1. Log in to the NetBoot/SUS/LP server web application. 2. Click **NetBoot Server** in the side navigation menu or in the mobile dropdown menu. 3. Upload a NetBoot image: * Click **Upload NetBoot Image**. * You will be connected to the SMB share where NetBoot images are stored. * Enter credentials for the SMB share and click **Connect**. * Copy a NetBoot image folder (.nbi extension) to the SMB share. The nbi folder must contain a .plist file and .dmg file to function properly. **Important:** The name of the folder cannot contain any spaces. 4. Return to the NetBoot/SUS/LP server web application and refresh the page. 5. Choose the NetBoot image from the pop-up menu. 6. **Optional:** Enter a name for your Netboot Server. The name cannot contain spaces. If left blank the name will default to the name of your .nbi folder uploaded previously. For common issues on this setting please see the troubleshooting section below. 7. Choose subnets for the NetBoot image by entering a subnet and a netmask. Then click **Add**. **Important**: One of the subnets must include the IP address of the NetBoot server. 8. Click **Enable NetBoot**. If NetBoot is successfully enabled, the NetBoot status alert turns green. ## Troubleshooting The best place to gather information on why your NetBoot Server might not be working is the "dhcpd" service logs in your system's default log locaion. For example: On Debian family distributions you would enter the command `grep "dhcpd" /var/log/syslog` On Red Hat family distributions you would enter the command `grep "dhcpd" /var/log/messages` Either of these commands will output a list of logs related to the dhcpd service to your console window. ## Using the NetBoot Server with the Casper Suite **Note**: The instructions in this section are for the Casper Suite v9.0 or later. However, if you are using the Casper Suite v8.x, these instructions can still be followed loosely. Like standard NetBoot servers, you can add the NetBoot server hosted by the NetBoot/SUS/LP server to the JSS. This allows you to use a policy or Casper Remote to boot managed computers to a NetBoot image. When adding the NetBoot server to the JSS, enter the IP address specified in the NetBoot/SUS/LP server web application and choose the “Use default image” option from the NetBoot Image pop-up menu. For more information on adding a NetBoot server to the JSS, see the “NetBoot Servers” section in the Casper Suite Administrator’s Guide. For more information on using a policy or Casper Remote to boot computers to a NetBoot image, see the “Booting Computers to NetBoot Images” section in the Casper Suite Administrator’s Guide. \ No newline at end of file +# Setting Up the NetBoot Server To set up a NetBoot server, you need a NetBoot image (.nbi folder). For more information, see the following Knowledge Base article: [Creating a NetBoot Image and Setting Up a NetBoot Server](https://www.jamf.com/jamf-nation/articles/307/creating-a-netboot-image-and-setting-up-a-netboot-server) 1. Log in to the NetBoot/SUS/LP server web application. 2. Click **NetBoot Server** in the side navigation menu or in the mobile dropdown menu. 3. Upload a NetBoot image: * Click **Upload NetBoot Image**. * You will be connected to the SMB share where NetBoot images are stored. * Enter credentials for the SMB share and click **Connect**. * Copy a NetBoot image folder (.nbi extension) to the SMB share. The nbi folder must contain a .plist file and .dmg file to function properly. **Important:** The name of the folder cannot contain any spaces. 4. Return to the NetBoot/SUS/LP server web application and refresh the page. 5. Select the radio button for the NetBoot image. 6. **Optional:** Click on the Image name to edit the Image properties. For common issues on this setting please see the troubleshooting section below. 7. Choose subnets for the NetBoot image by entering a subnet and a netmask. Then click **Add**. **Important**: One of the subnets must include the IP address of the NetBoot server. 8. Click **Enable NetBoot**. If NetBoot is successfully enabled, the NetBoot status alert turns green. ## Troubleshooting The best place to gather information on why your NetBoot Server might not be working is the "dhcpd" service logs in your system's default log locaion. For example: On Debian family distributions you would enter the command `grep "dhcpd" /var/log/syslog` On Red Hat family distributions you would enter the command `grep "dhcpd" /var/log/messages` Either of these commands will output a list of logs related to the dhcpd service to your console window. ## Using the NetBoot Server with the Casper Suite **Note**: The instructions in this section are for the Casper Suite v9.0 or later. However, if you are using the Casper Suite v8.x, these instructions can still be followed loosely. Like standard NetBoot servers, you can add the NetBoot server hosted by the NetBoot/SUS/LP server to the JSS. This allows you to use a policy or Casper Remote to boot managed computers to a NetBoot image. When adding the NetBoot server to the JSS, enter the IP address specified in the NetBoot/SUS/LP server web application and choose the “Use default image” option from the NetBoot Image pop-up menu. For more information on adding a NetBoot server to the JSS, see the “NetBoot Servers” section in the Casper Suite Administrator’s Guide. For more information on using a policy or Casper Remote to boot computers to a NetBoot image, see the “Booting Computers to NetBoot Images” section in the Casper Suite Administrator’s Guide. \ No newline at end of file diff --git a/docs/settings.md b/docs/settings.md index ee7cf58..842cf65 100755 --- a/docs/settings.md +++ b/docs/settings.md @@ -98,10 +98,48 @@ Certificates Settings allows you to modify the server settings with either a Tom 3. In the "NetBoot/SUS/LDAP Proxy Server" section, click **Certificates** . -4. Enter the "Private Key", "Certificate", and "Chain" fields with the appropriate unencrypted certificate information. +4. If you wish to create a CSR, update the Common Name field and click "Create". A zip archive will download containing a new private key and related signing request. + +5. Enter the "Private Key", "Certificate", and "Chain" fields with the appropriate unencrypted certificate information. -5. Click **Save**. +6. Click **Save**. + +7. Restart NetBoot/SUS/LP Server. + + +## Logs Settings +The Logs settings allows you to select and view the system log files on the NetBoot/SUS/LP Server. + +1. Log in to the NetBoot/SUS/LP server web application. + +2. In the side navigation menu or in the mobile dropdown menu, click **Settings** . + +3. In the "NetBoot/SUS/LDAP Proxy Server" section, click **Logs** . + +4. Select the log file you wish to view. + +5. Enter the number of lines (from the end) of the log file you wish to see. If this is left blank, the entire log is displayed. + +5. Click **Display**. + + +## Storage Settings +The Storage settings allows you to expand the logical disk volume on NetBoot/SUS/LP Server, if the VMDK has been expanded. + +1. Shut down the NetBoot/SUS/LP Server. + +2. Expand the VMDK of the NetBoot/SUS/LP Server from within the hypervisor. + +3. Start up the NetBoot/SUS/LP Server. + +4. Log in to the NetBoot/SUS/LP server web application. + +5. In the side navigation menu or in the mobile dropdown menu, click **Settings** . + +6. In the "NetBoot/SUS/LDAP Proxy Server" section, click **Storage** . + +7. If there is sufficient space available, the Resize button will be enabled, click **Resize**. -6. Restart NetBoot/SUS/LP Server. +8. Restart the NetBoot/SUS/LP Server for the additional storage to become available. diff --git a/docs/sus.md b/docs/sus.md index b6c9b3f..71960aa 100755 --- a/docs/sus.md +++ b/docs/sus.md @@ -125,6 +125,14 @@ Branch URLs vary depending on the operating system of enrolled computers. Exampl http://sus.mycompany.corp/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1_.sucatalog +**macOS v10.12** + + http://sus.mycompany.corp/content/catalogs/others/index-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1_.sucatalog + +**macOS v10.13** + + http://sus.mycompany.corp/content/catalogs/others/index-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1_.sucatalog + ### Running Software Update on Computers For instructions on using the Casper Suite to run Software Update on computers that are managed by the JSS, see the “Running Software Update” section in the Casper Suite Administrator’s Guide. diff --git a/includes/logger.sh b/includes/logger.sh old mode 100644 new mode 100755 index ec3c243..30d28c4 --- a/includes/logger.sh +++ b/includes/logger.sh @@ -2,27 +2,38 @@ export logFile="/var/appliance/logs/applianceinstaller.log" -# Logger function -logEvent(){ - if [ ! -f "$logFile" ]; then - mkdir -p "$(dirname $logFile)" - fi - echo $1 - echo $(date "+[%Y-%m-%d %H:%M:%S]: ") $1 >> $logFile +mkLogDir() { + if [ ! -f "$logFile" ] + then + mkdir -p "$(dirname $logFile)" + fi } -logEventCritical(){ - if [ ! -f "$logFile" ]; then - mkdir -p "$(dirname $logFile)" - fi - echo $1 - echo $(date "+[%Y-%m-%d %H:%M:%S]: ") $1 >> $logFile +timestamp() { + date "+[%Y-%m-%d %H:%M:%S]: " } -logEventNoNewLine(){ - if [ ! -f "$logFile" ]; then - mkdir -p "$(dirname $logFile)" - fi - echo -n $1 - echo $(date "+[%Y-%m-%d %H:%M:%S]: ") $1 >> $logFile +logToFile() { + echo "$(timestamp)" "$1" >> $logFile } + +log(){ + mkLogDir + + if [[ $INTERACTIVE = true ]] + then + echo "$@" + fi + + logToFile "${@: -1}" +} + +logCritical(){ + INTERACTIVE=true log "$1" +} + +logNoNewLine(){ + log -n "$1" +} + +export -f log logCritical logNoNewLine logToFile timestamp mkLogDir diff --git a/webadmin/var/appliance/dialog.sh b/webadmin/var/appliance/dialog.sh index e5cef94..8436e3c 100755 --- a/webadmin/var/appliance/dialog.sh +++ b/webadmin/var/appliance/dialog.sh @@ -26,7 +26,7 @@ message="\n |--|--|\n || ||\n ooO Ooo\n\n\n -For more information visit https://jamfnation.jamfsoftware.com/\n" +For more information visit https://www.jamf.com/jamf-nation/\n" okpressed=0 while [ "$okpressed" != "1" ] do diff --git a/webadmin/var/www/webadmin/AFP.php b/webadmin/var/www/webadmin/AFP.php old mode 100644 new mode 100755 index 5c731d7..cd48116 --- a/webadmin/var/www/webadmin/AFP.php +++ b/webadmin/var/www/webadmin/AFP.php @@ -8,6 +8,8 @@ include "inc/header.php"; +$afp_running = (trim(suExec("getafpstatus")) === "true"); + $accounterror = ""; $accountsuccess = ""; @@ -38,6 +40,7 @@ $accounterror = "All fields required."; } } + ?> " ?> + +
@@ -59,20 +91,43 @@
+
+ + + Enabled + +
"; + } + else + { + echo "
+ Disabled + +
"; + } + ?> +
+ + + AFP Password - + - +
- +

diff --git a/webadmin/var/www/webadmin/SMB.php b/webadmin/var/www/webadmin/SMB.php index 3e9b325..92ec611 100644 --- a/webadmin/var/www/webadmin/SMB.php +++ b/webadmin/var/www/webadmin/SMB.php @@ -8,6 +8,8 @@ include "inc/header.php"; +$smb_running = (trim(suExec("getsmbstatus")) === "true"); + $accounterror = ""; $accountsuccess = ""; @@ -48,23 +50,76 @@ " . $accountsuccess . "
" ?> + +

SMB

+
+
+ + + Enabled + +
"; + } + else + { + echo "
+ Disabled + +
"; + } + ?> + + + + SMB Password - + - +
diff --git a/webadmin/var/www/webadmin/SUS.php b/webadmin/var/www/webadmin/SUS.php index d5c63c6..8689bb0 100644 --- a/webadmin/var/www/webadmin/SUS.php +++ b/webadmin/var/www/webadmin/SUS.php @@ -57,19 +57,95 @@ suExec("setbaseurl ".$conf->getSetting("susbaseurl")); } } + +if (isset($_POST['apply_proxy'])) +{ + if (empty($_POST['proxy_host']) && empty($_POST['proxy_user'])) + { + suExec("setsusproxy"); + } + if (!empty($_POST['proxy_host']) && empty($_POST['proxy_user'])) + { + suExec("setsusproxy ".$_POST['proxy_host']." ".$_POST['proxy_port']); + } + if (!empty($_POST['proxy_host']) && !empty($_POST['proxy_user'])) + { + suExec("setsusproxy ".$_POST['proxy_host']." ".$_POST['proxy_port']." ".$_POST['proxy_user']." ".$_POST['proxy_pass']); + } +} + +$susProxyHost = trim(suExec("getsusproxyhost")); +$susProxyPort = trim(suExec("getsusproxyport")); +$susProxyUser = trim(suExec("getsusproxyuser")); +$susProxyPassword = trim(suExec("getsusproxypass")); + // #################################################################### // End of GET/POST parsing // #################################################################### ?>

Software Update Server

@@ -89,7 +165,7 @@ function validateField(fieldid, buttonid) Base URL for the software update server (e.g. "http://sus.mycompany.corp")
- " onKeyUp="validateField('baseurl', 'setbaseurl');" onChange="validateField('baseurl', 'setbaseurl');"/> + " onClick="validateBaseURL();" onKeyUp="validateBaseURL();" onChange="validateBaseURL();"/> @@ -131,7 +207,7 @@ function validateField(fieldid, buttonid) New Branch
- + @@ -173,6 +249,8 @@ function validateField(fieldid, buttonid) + + @@ -183,6 +261,57 @@ function validateField(fieldid, buttonid) Last Sync: +
+
+ +
+
+ Proxy Configuration +
+ +
+ +
+ +
+ +
+
Host
+ +
+ +
+ +
+
Port
+ +
+ +
+ +
+ +
+ +
+
Username
+ +
+ +
+ +
+
Password
+ +
+ +
+ + +
+
diff --git a/webadmin/var/www/webadmin/about.php b/webadmin/var/www/webadmin/about.php index 0ad1acc..0075392 100644 --- a/webadmin/var/www/webadmin/about.php +++ b/webadmin/var/www/webadmin/about.php @@ -4,6 +4,9 @@ include "inc/functions.php"; $title = "About"; include "inc/header.php"; +$os_name = trim(suExec("getName")); +$home_url = trim(suExec("getHomeUrl")); +$install_type = trim(suExec("getInstallType")); ?> + +

Logs

-
- +
+
+ +
+ +
+ +
+ +
+
+ Display Log +
+ +
+ +
+
Select Log File
+ +
+ +
+ +
+
Number of Lines
+ +
+ +
+ + +
+ + + + +
+
+ + + +
+ +
+
+ \ No newline at end of file diff --git a/webadmin/var/www/webadmin/logsCtl.php b/webadmin/var/www/webadmin/logsCtl.php new file mode 100644 index 0000000..bb7d461 --- /dev/null +++ b/webadmin/var/www/webadmin/logsCtl.php @@ -0,0 +1,47 @@ + +

Display Log

+
+
+
+
+
+
+
+ ".$_GET['log'].""; + print "
".$logcontent."
"; + ?> +
+
+
+
+
+
+
+ +
+
+ \ No newline at end of file diff --git a/webadmin/var/www/webadmin/managenbi.php b/webadmin/var/www/webadmin/managenbi.php new file mode 100644 index 0000000..181537c --- /dev/null +++ b/webadmin/var/www/webadmin/managenbi.php @@ -0,0 +1,212 @@ +getSetting("netbootimage"); + $wasrunning = getNetBootStatus(); + if ($image == $curimg && $wasrunning) + { + $nbconf = file_get_contents("/var/appliance/conf/dhcpd.conf"); + $nbsubnets = ""; + foreach($conf->getSubnets() as $key => $value) + { + $nbsubnets .= "subnet ".$value['subnet']." netmask ".$value['netmask']." {\n\tallow unknown-clients;\n}\n\n"; + } + $nbconf = str_replace("##SUBNETS##", $nbsubnets, $nbconf); + suExec("touchconf \"/var/appliance/conf/dhcpd.conf.new\""); + if(file_put_contents("/var/appliance/conf/dhcpd.conf.new", $nbconf) === FALSE) + { + echo "
ERROR: Unable to update dhcpd.conf
"; + + } + suExec("disablenetboot"); + suExec("installdhcpdconf"); + suExec("setnbimages ".$image); + } +} + +if ($image != "") { + $Name = trim(suExec("getNBIproperty ".$image." Name")); + $Description = trim(suExec("getNBIproperty ".$image." Description")); + $Type = trim(suExec("getNBIproperty ".$image." Type")); + $Index = trim(suExec("getNBIproperty ".$image." Index")); + $SupportsDiskless = trim(suExec("getNBIproperty ".$image." SupportsDiskless")); + $imageType = trim(suExec("getNBIproperty ".$image." imageType")); + + if ($Name == "") { + $Name = str_replace(".nbi", "" , $image); + $errorMessage = "WARNING: Unable to read NBImageInfo.plist default values are being used"; + } + if ($Type == "") { $Type = "HTTP"; } + if ($Index == "") { $Index = rand(1, 4095); } + if ($SupportsDiskless == "") { $SupportsDiskless = "False"; } + if ($imageType == "") { $imageType = "netboot"; } +} + +?> + + + +$errorMessage
"; +} +else if ($statusMessage != "") +{ + echo "
$statusMessage
"; +} +?> + +
+
+ +

+ +
+ +
+ + Choose Image + + + +
+ +
+
+ Image Properties +
+ +
+
+
Network Disk
+ This name identifies the image in the Startup Disk preferences pane on client computers + /> +
+ +
+ +
+
Description
+ (Optional) Notes or other information to help you characterize the image + +
+ +
+ +
+
Make available over
+ By default, images are available over HTTP + +
+ +
+ +
+
Image Index
+ 1-4095 indicates a local image unique to this server + /> +
+ +
+ +
+ +
+
+ + + +
+ +
+ +
+
+ + +
+
+ + diff --git a/webadmin/var/www/webadmin/netBoot.php b/webadmin/var/www/webadmin/netBoot.php index e4fa386..92c3105 100644 --- a/webadmin/var/www/webadmin/netBoot.php +++ b/webadmin/var/www/webadmin/netBoot.php @@ -15,26 +15,20 @@ $netbootimgdir = "/srv/NetBoot/NetBootSP0/"; $subnetcheck = $conf->getSubnets(); -if (isset($_POST['netbootName'])) -{ - $conf->setSetting("netbootname", $_POST['netbootName']); -} - -if ((isset($_POST['enablenetboot']) || isset($_POST['changenetboot'])) && empty($subnetcheck)) +if (isset($_POST['enablenetboot']) && empty($subnetcheck)) { echo "
ERROR: Ensure you added a proper Subnet and Netmask
"; } -if ((isset($_POST['enablenetboot']) || isset($_POST['changenetboot'])) && !isset($_POST['NetBootImage'])) +if (isset($_POST['enablenetboot']) && (!isset($_POST['NetBootImage']) || $_POST['NetBootImage'] == "")) { - echo "
ERROR: Ensure you have uploaded a properly configured NetBoot image
"; + echo "
ERROR: Ensure you have uploaded and selected a properly configured NetBoot image
"; } -if (isset($_POST['NetBootImage'])) +if (isset($_POST['NetBootImage']) && $_POST['NetBootImage'] != "") { $wasrunning = getNetBootStatus(); - $netbootname = $conf->getSetting("netbootname"); $nbi = $_POST['NetBootImage']; if ($nbi != "") { @@ -54,12 +48,12 @@ suExec("disablenetboot"); suExec("installdhcpdconf"); - if ($wasrunning || isset($_POST['enablenetboot']) || isset($_POST['changenetboot'])) { - suExec("setnbimages " . $nbi . " " . $netbootname); + if ($wasrunning || isset($_POST['enablenetboot'])) { + suExec("setnbimages " . $nbi); } $conf->setSetting("netbootimage", $nbi); - if ((isset($_POST['enablenetboot']) || isset($_POST['changenetboot'])) && !getNetBootStatus() && !empty($subnetcheck)) { + if (isset($_POST['enablenetboot']) && !getNetBootStatus() && !empty($subnetcheck)) { echo "
ERROR: Unable to start NetBoot service. Ensure your .nbi directory is properly configured
"; } } @@ -127,20 +121,57 @@ } } +if (!isset($_POST['disablenetboot']) && getNetBootStatus()) +{ + $tftp_running = (trim(suExec("gettftpstatus")) === "true"); + $nfs_running = (trim(suExec("getnfsstatus")) === "true"); + $afp_running = (trim(suExec("getafpstatus")) === "true"); + if (!$tftp_running) + { + echo "
ERROR: TFTP is not running, restart NetBoot
"; + } + if (!$nfs_running) + { + echo "
ERROR: NFS is not running, restart NetBoot
"; + } + if (!$afp_running) + { + echo "
WARNING: AFP is not running, diskless will be unavailable
"; + } +} + // #################################################################### // End of GET/POST parsing // #################################################################### ?>

NetBoot Server

@@ -155,7 +186,7 @@ function validateSubnet()
Enabled @@ -179,54 +210,54 @@ function validateSubnet()

-
+
- NetBoot Image and Name + NetBoot Images
-
-
-
Image
- NetBoot image that computers boot to - onChange="document.getElementById('NetBootImage').value = this.value; javascript:ajaxPost('ajax.php?service=NetBoot', 'NetBootImage='+this.value);"/> + + + + + + - -
- -
- -
-
Name
- (Optional) NetBoot name to appear on receiving boot devices. Defaults to the .nbi folder name. Cannot contain spaces - " /> -
-
- - - + $i++; + } + ?> + +
+ +
Netboot Subnet and Netmask @@ -237,14 +268,14 @@ function validateSubnet()
Subnet
One of the subnets must include the IP address of the NetBoot server - getSubnets())) { echo $currentSubnet; } ?>" onKeyUp="validateSubnet();" onChange="validateSubnet();" /> + getSubnets())) { echo $currentSubnet; } ?>" onClick="validateSubnet();" onKeyUp="validateSubnet();" onChange="validateSubnet();" />

Netmask
- getSubnets())) { echo $currentNetmask; } ?>" onKeyUp="validateSubnet();" onChange="validateSubnet();" /> + getSubnets())) { echo $currentNetmask; } ?>" onClick="validateSubnet();" onKeyUp="validateSubnet();" onChange="validateSubnet();" />
@@ -268,7 +299,7 @@ function validateSubnet() "> - Delete + Delete diff --git a/webadmin/var/www/webadmin/networkSettings.php b/webadmin/var/www/webadmin/networkSettings.php index b444c7f..d507225 100644 --- a/webadmin/var/www/webadmin/networkSettings.php +++ b/webadmin/var/www/webadmin/networkSettings.php @@ -35,8 +35,10 @@ && $_POST['gateway'] != $_POST['ip'] && isValidIPAddress($_POST['dns1']) && (isValidIPAddress($_POST['dns2']) || $_POST['dns2'] == "")) { - //address netmask gateway - suExec("setip ".$_POST['ip']." ".$_POST['netmask']." ".$_POST['gateway']); + // 2017-03-07: NetSUS Bug Fix + // Updated to correctly set static DNS + //address netmask gateway dns1 dns2 + suExec("setip ".$_POST['ip']." ".$_POST['netmask']." ".$_POST['gateway']." ".$_POST['dns1']." ".$_POST['dns2']); suExec("setdns ".$_POST['dns1']." ".$_POST['dns2']); } } @@ -78,6 +80,41 @@ ?>