1#!/bin/bash 2# Program to build and sign debian packages, and upload those to a public reprepro repository. 3# Copyright (c) 2015 Santiago Bassett <santiago.bassett@gmail.com> 4 5# This program is free software; you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation; either version 3 of the License, or 8# (at your option) any later version. 9 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14 15# You should have received a copy of the GNU General Public License 16# along with this program; if not, write to the Free Software Foundation, 17# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 19# 20# CONFIGURATION VARIABLES 21# 22 23ossec_version='2.8.2' 24source_file="ossec-hids-${ossec_version}.tar.gz" 25#packages=(ossec-hids ossec-hids-agent) # only options available 26packages=(ossec-hids ossec-hids-agent) 27 28# codenames=(sid jessie wheezy precise trusty utopic) 29codenames=(sid jessie wheezy precise trusty utopic) 30 31# For Debian use: sid, jessie or wheezy (hardcoded in update_changelog function) 32# For Ubuntu use: lucid, precise, trusty or utopic 33codenames_ubuntu=(precise trusty utopic) 34codenames_debian=(sid jessie wheezy) 35 36# architectures=(amd64 i386) only options available 37architectures=(amd64 i386) 38 39# GPG key 40signing_key='XXXX' 41signing_pass='XXXX' 42 43# Debian files 44debian_files_path="/home/ubuntu/debian_files" 45 46# Setting up logfile 47scriptpath=$( cd $(dirname $0) ; pwd -P ) 48logfile=$scriptpath/ossec_packages.log 49 50 51# 52# Function to write to LOG_FILE 53# 54write_log() 55{ 56 if [ ! -e "$logfile" ] ; then 57 touch "$logfile" 58 fi 59 while read text 60 do 61 local logtime=`date "+%Y-%m-%d %H:%M:%S"` 62 echo $logtime": $text" | tee -a $logfile; 63 done 64} 65 66 67# 68# Check if element is in an array 69# Arguments: element array 70# 71contains_element() { 72 local e 73 for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done 74 return 1 75} 76 77 78# 79# Show help function 80# 81show_help() 82{ 83 echo " 84 This tool can be used to generate OSSEC packages for Ubuntu and Debian. 85 86 CONFIGURATION: The script is currently configured with the following variables: 87 * Packages: ${packages[*]}. 88 * Distributions: ${codenames[*]}. 89 * Architectures: ${architectures[*]}. 90 * OSSEC version: ${ossec_version}. 91 * Source file: ${source_file}. 92 * Signing key: ${signing_key}. 93 94 USAGE: Command line arguments available: 95 -h | --help Displays this help. 96 -u | --update Updates chroot environments. 97 -d | --download Downloads source file and prepares source directories. 98 -b | --build Builds deb packages. 99 -s | --sync Synchronizes with the apt-get repository. 100 " 101} 102 103 104# 105# Reads latest package version from changelog file 106# Argument: changelog_file 107# 108read_package_version() 109{ 110 if [ ! -e "$1" ] ; then 111 echo "Error: Changelog file $1 does not exist" | write_log 112 exit 1 113 fi 114 local regex="^ossec-hids[A-Za-z-]* \([0-9]+.*[0-9]*.*[0-9]*-([0-9]+)[A-Za-z]*\)" 115 while read line 116 do 117 if [[ $line =~ $regex ]]; then 118 package_version="${BASH_REMATCH[1]}" 119 break 120 fi 121 done < $1 122 local check_regex='^[0-9]+$' 123 if ! [[ ${package_version} =~ ${check_regex} ]]; then 124 echo "Error: Package version could not be read from $1" | write_log 125 exit 1 126 fi 127} 128 129 130# 131# Updates changelog file with new codename, date and debdist. 132# Arguments: changelog_file codename 133# 134update_changelog() 135{ 136 local changelog_file=$1 137 local changelog_file_tmp="${changelog_file}.tmp" 138 local codename=$2 139 140 if [ ! -e "$1" ] ; then 141 echo "Error: Changelog file $1 does not exist" | write_log 142 exit 1 143 fi 144 145 local check_codenames=( ${codenames_debian[*]} ${codenames_ubuntu[*]} ) 146 if ! contains_element $codename ${check_codenames[*]} ; then 147 echo "Error: Codename $codename not contained in codenames for Debian or Ubuntu" | write_log 148 exit 1 149 fi 150 151 # For Debian 152 if [ $codename = "sid" ]; then 153 local debdist="unstable" 154 elif [ $codename = "jessie" ]; then 155 local debdist="testing" 156 elif [ $codename = "wheezy" ]; then 157 local debdist="stable" 158 fi 159 160 # For Ubuntu 161 if contains_element $codename ${codenames_ubuntu[*]} ; then 162 local debdist=$codename 163 fi 164 165 # Modifying file 166 local changelogtime=$(date -R) 167 local last_date_changed=0 168 169 local regex1="^(ossec-hids[A-Za-z-]* \([0-9]+.*[0-9]*.*[0-9]*-[0-9]+)[A-Za-z]*\)" 170 local regex2="( -- [[:alnum:]]*[^>]*> )[[:alnum:]]*," 171 172 if [ -f ${changelog_file_tmp} ]; then 173 rm -f ${changelog_file_tmp} 174 fi 175 touch ${changelog_file_tmp} 176 177 IFS='' #To preserve line leading whitespaces 178 while read line 179 do 180 if [[ $line =~ $regex1 ]]; then 181 line="${BASH_REMATCH[1]}$codename) $debdist; urgency=low" 182 fi 183 if [[ $line =~ $regex2 ]] && [ $last_date_changed -eq 0 ]; then 184 line="${BASH_REMATCH[1]}$changelogtime" 185 last_date_changed=1 186 fi 187 echo "$line" >> ${changelog_file_tmp} 188 done < ${changelog_file} 189 190 mv ${changelog_file_tmp} ${changelog_file} 191} 192 193 194# 195# Update chroot environments 196# 197update_chroots() 198{ 199 for codename in ${codenames[@]} 200 do 201 for arch in ${architectures[@]} 202 do 203 echo "Updating chroot environment: ${codename}-${arch}" | write_log 204 if sudo DIST=$codename ARCH=$arch pbuilder update ; then 205 echo "Successfully updated chroot environment: ${codename}-${arch}" | write_log 206 else 207 echo "Error: Problem detected updating chroot environment: ${codename}-${arch}" | write_log 208 fi 209 done 210 done 211} 212 213 214# 215# Downloads packages and prepare source directories. 216# This is needed before building the packages. 217# 218download_source() 219{ 220 221 # Checking that Debian files exist for this version 222 for package in ${packages[*]} 223 do 224 if [ ! -d ${debian_files_path}/${ossec_version}/$package/debian ]; then 225 echo "Error: Couldn't find debian files directory for $package, version ${ossec_version}" | write_log 226 exit 1 227 fi 228 done 229 230 # Downloading file 231 if wget -O $scriptpath/${source_file} -U ossec https://github.com/ossec/ossec-hids/archive/${ossec_version}.tar.gz ; then 232 echo "Successfully downloaded source file ${source_file} from ossec.net" | write_log 233 else 234 echo "Error: File ${source_file} was could not be downloaded" | write_log 235 exit 1 236 fi 237 238 # Uncompressing files 239 tmp_directory=$(echo ${source_file} | sed -e 's/.tar.gz$//') 240 if [ -d ${scriptpath}/${tmp_directory} ]; then 241 echo " + Deleting previous directory ${scriptpath}/${tmp_directory}" | write_log 242 sudo rm -rf ${scriptpath}/${tmp_directory} 243 fi 244 tar -xvzf ${scriptpath}/${source_file} 245 if [ ! -d ${scriptpath}/${tmp_directory} ]; then 246 echo "Error: Couldn't find uncompressed directory, named ${tmp_directory}" | write_log 247 exit 1 248 fi 249 250 # Organizing directories structure 251 for package in ${packages[*]} 252 do 253 if [ -d ${scriptpath}/$package ]; then 254 echo " + Deleting previous source directory ${scriptpath}/$package" | write_log 255 sudo rm -rf ${scriptpath}/$package 256 fi 257 mkdir $scriptpath/$package 258 cp -pr $scriptpath/${tmp_directory} $scriptpath/$package/$package-${ossec_version} 259 cp -p $scriptpath/${source_file} $scriptpath/$package/${package}_${ossec_version}.orig.tar.gz 260 cp -pr ${debian_files_path}/${ossec_version}/$package/debian $scriptpath/$package/${package}-${ossec_version}/debian 261 done 262 rm -rf $scriptpath/${tmp_directory} 263 264 echo "The packages directories for ${packages[*]} version ${ossec_version} have been successfully prepared." | write_log 265} 266 267 268# 269# Build packages 270# 271build_packages() 272{ 273 274for package in ${packages[@]} 275do 276 for codename in ${codenames[@]} 277 do 278 for arch in ${architectures[@]} 279 do 280 281 echo "Building Debian package ${package} ${codename}-${arch}" | write_log 282 283 local source_path="$scriptpath/${package}/${package}-${ossec_version}" 284 local changelog_file="${source_path}/debian/changelog" 285 if [ ! -f ${changelog_file} ] ; then 286 echo "Error: Couldn't find changelog file for ${package}-${ossec_version}" | write_log 287 exit 1 288 fi 289 290 # Updating changelog file with new codename, date and debdist. 291 if update_changelog ${changelog_file} ${codename} ; then 292 echo " + Changelog file ${changelog_file} updated for $package ${codename}-${arch}" | write_log 293 else 294 echo "Error: Changelog file ${changelog_file} for $package ${codename}-${arch} could not be updated" | write_log 295 exit 1 296 fi 297 298 # Setting up global variable package_version, used for deb_file and changes_file 299 read_package_version ${changelog_file} 300 local deb_file="${package}_${ossec_version}-${package_version}${codename}_${arch}.deb" 301 local changes_file="${package}_${ossec_version}-${package_version}${codename}_${arch}.changes" 302 local dsc_file="${package}_${ossec_version}-${package_version}${codename}.dsc" 303 local results_dir="/var/cache/pbuilder/${codename}-${arch}/result/${package}" 304 local base_tgz="/var/cache/pbuilder/${codename}-${arch}-base.tgz" 305 local cache_dir="/var/cache/pbuilder/${codename}-${arch}/aptcache" 306 307 # Creating results directory if it does not exist 308 if [ ! -d ${results_dir} ]; then 309 sudo mkdir -p ${results_dir} 310 fi 311 312 # Building the package 313 cd ${source_path} 314 if sudo /usr/bin/pdebuild --use-pdebuild-internal --architecture ${arch} --buildresult ${results_dir} -- --basetgz \ 315 ${base_tgz} --distribution ${codename} --architecture ${arch} --aptcache ${cache_dir} --override-config ; then 316 echo " + Successfully built Debian package ${package} ${codename}-${arch}" | write_log 317 else 318 echo "Error: Could not build package $package ${codename}-${arch}" | write_log 319 exit 1 320 fi 321 322 # Checking that resulting debian package exists 323 if [ ! -f ${results_dir}/${deb_file} ] ; then 324 echo "Error: Could not find ${results_dir}/${deb_file}" | write_log 325 exit 1 326 fi 327 328 # Checking that package has at least 50 files to confirm it has been built correctly 329 local files=$(sudo /usr/bin/dpkg --contents ${results_dir}/${deb_file} | wc -l) 330 if [ "${files}" -lt "50" ]; then 331 echo "Error: Package ${package} ${codename}-${arch} contains only ${files} files" | write_log 332 echo "Error: Check that the Debian package has been built correctly" | write_log 333 exit 1 334 else 335 echo " + Package ${results_dir}/${deb_file} ${codename}-${arch} contains ${files} files" | write_log 336 fi 337 338 # Signing Debian package 339 if [ ! -f "${results_dir}/${changes_file}" ] || [ ! -f "${results_dir}/${dsc_file}" ] ; then 340 echo "Error: Could not find dsc and changes file in ${results_dir}" | write_log 341 exit 1 342 fi 343 sudo /usr/bin/expect -c " 344 spawn sudo debsign --re-sign -k${signing_key} ${results_dir}/${changes_file} 345 expect -re \".*Enter passphrase:.*\" 346 send \"${signing_pass}\r\" 347 expect -re \".*Enter passphrase:.*\" 348 send \"${signing_pass}\r\" 349 expect -re \".*Successfully signed dsc and changes files.*\" 350 " 351 if [ $? -eq 0 ] ; then 352 echo " + Successfully signed Debian package ${changes_file} ${codename}-${arch}" | write_log 353 else 354 echo "Error: Could not sign Debian package ${changes_file} ${codename}-${arch}" | write_log 355 exit 1 356 fi 357 358 # Verifying signed changes and dsc files 359 if sudo gpg --verify "${results_dir}/${dsc_file}" && sudo gpg --verify "${results_dir}/${changes_file}" ; then 360 echo " + Successfully verified GPG signature for files ${dsc_file} and ${changes_file}" | write_log 361 else 362 echo "Error: Could not verify GPG signature for ${dsc_file} and ${changes_file}" | write_log 363 exit 1 364 fi 365 366 echo "Successfully built and signed Debian package ${package} ${codename}-${arch}" | write_log 367 368 done 369 done 370done 371} 372 373# Synchronizes with the external repository, uploading new packages and ubstituting old ones. 374sync_repository() 375{ 376for package in ${packages[@]} 377do 378 for codename in ${codenames[@]} 379 do 380 for arch in ${architectures[@]} 381 do 382 383 # Reading package version from changelog file 384 local source_path="$scriptpath/${package}/${package}-${ossec_version}" 385 local changelog_file="${source_path}/debian/changelog" 386 if [ ! -f ${changelog_file} ] ; then 387 echo "Error: Couldn't find ${changelog_file} for package ${package} ${codename}-${arch}" | write_log 388 exit 1 389 fi 390 391 # Setting up global variable package_version, used for deb_file and changes_file. 392 read_package_version ${changelog_file} 393 local deb_file="${package}_${ossec_version}-${package_version}${codename}_${arch}.deb" 394 local changes_file="${package}_${ossec_version}-${package_version}${codename}_${arch}.changes" 395 local results_dir="/var/cache/pbuilder/${codename}-${arch}/result/${package}" 396 if [ ! -f ${results_dir}/${deb_file} ] || [ ! -f ${results_dir}/${changes_file} ] ; then 397 echo "Error: Couldn't find ${deb_file} or ${changes_file}" | write_log 398 exit 1 399 fi 400 401 # Uploading package to repository 402 cd ${results_dir} 403 echo "Uploading package ${changes_file} for ${codename} to OSSEC repository" | write_log 404 if sudo /usr/bin/dupload --nomail -f --to ossec-repository ${changes_file} ; then 405 echo " + Successfully uploaded package ${changes_file} for ${codename} to OSSEC repository" | write_log 406 else 407 echo "Error: Could not upload package ${changes_file} for ${codename} to the repository" | write_log 408 exit 1 409 fi 410 411 # Checking if it is an Ubuntu package 412 if contains_element $codename ${codenames_ubuntu[*]} ; then 413 local is_ubuntu=1 414 else 415 local is_ubuntu=0 416 fi 417 418 # Moving package to the right directory at the OSSEC apt repository server 419 echo " + Adding package /opt/incoming/${deb_file} to server repository for ${codename} distribution" | write_log 420 if [ $is_ubuntu -eq 1 ]; then 421 remove_package="cd /var/www/repos/apt/ubuntu; reprepro -A ${arch} remove ${codename} ${package}" 422 include_package="cd /var/www/repos/apt/ubuntu; reprepro includedeb ${codename} /opt/incoming/${deb_file}" 423 else 424 remove_package="cd /var/www/repos/apt/debian; reprepro -A ${arch} remove ${codename} ${package}" 425 include_package="cd /var/www/repos/apt/debian; reprepro includedeb ${codename} /opt/incoming/${deb_file}" 426 fi 427 428 /usr/bin/expect -c " 429 spawn sudo ssh root@ossec-repository \"${remove_package}\" 430 expect -re \"Not removed as not found.*\" { exit 1 } 431 expect -re \".*enter passphrase:.*\" { send \"${signing_pass}\r\" } 432 expect -re \".*enter passphrase:.*\" { send \"${signing_pass}\r\" } 433 expect -re \".*deleting.*\" 434 " 435 436 /usr/bin/expect -c " 437 spawn sudo ssh root@ossec-repository \"${include_package}\" 438 expect -re \"Skipping inclusion.*\" { exit 1 } 439 expect -re \".*enter passphrase:.*\" 440 send \"${signing_pass}\r\" 441 expect -re \".*enter passphrase:.*\" 442 send \"${signing_pass}\r\" 443 expect -re \".*Exporting.*\" 444 " 445 echo "Successfully added package ${deb_file} to server repository for ${codename} distribution" | write_log 446 done 447 done 448done 449} 450 451 452# If there are no arguments, display help 453if [ $# -eq 0 ]; then 454 show_help 455 exit 0 456fi 457 458# Reading command line arguments 459while [[ $# > 0 ]] 460do 461key="$1" 462shift 463 464case $key in 465 -h|--help) 466 show_help 467 exit 0 468 ;; 469 -u|--update) 470 update_chroots 471 shift 472 ;; 473 -d|--download) 474 download_source 475 shift 476 ;; 477 -b|--build) 478 build_packages 479 shift 480 ;; 481 -s|--sync) 482 sync_repository 483 shift 484 ;; 485 *) 486 echo "Unknown command line argument." 487 show_help 488 exit 0 489 ;; 490 esac 491done 492 493# vim: tabstop=2 expandtab shiftwidth=2 softtabstop=2 494