new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / contrib / debian-packages / generate_ossec.sh
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
23 ossec_version='2.8.2'
24 source_file="ossec-hids-${ossec_version}.tar.gz"
25 #packages=(ossec-hids ossec-hids-agent) # only options available
26 packages=(ossec-hids ossec-hids-agent)
27
28 # codenames=(sid jessie wheezy precise trusty utopic) 
29 codenames=(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
33 codenames_ubuntu=(precise trusty utopic)
34 codenames_debian=(sid jessie wheezy)
35
36 # architectures=(amd64 i386) only options available
37 architectures=(amd64 i386)
38
39 # GPG key
40 signing_key='XXXX'
41 signing_pass='XXXX'
42
43 # Debian files
44 debian_files_path="/home/ubuntu/debian_files"
45
46 # Setting up logfile
47 scriptpath=$( cd $(dirname $0) ; pwd -P )
48 logfile=$scriptpath/ossec_packages.log
49
50
51 #
52 # Function to write to LOG_FILE
53 #
54 write_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 #
71 contains_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 #
81 show_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 #
108 read_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 #
134 update_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 #
197 update_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 #
218 download_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 #
271 build_packages()
272 {
273
274 for package in ${packages[@]}
275 do 
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
370 done
371 }
372
373 # Synchronizes with the external repository, uploading new packages and ubstituting old ones.
374 sync_repository()
375 {
376 for package in ${packages[@]}
377 do
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
448 done
449 }
450
451
452 # If there are no arguments, display help
453 if [ $# -eq 0 ]; then
454   show_help
455   exit 0
456 fi
457
458 # Reading command line arguments
459 while [[ $# > 0 ]]
460 do
461 key="$1"
462 shift
463
464 case $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
491 done
492
493 # vim: tabstop=2 expandtab shiftwidth=2 softtabstop=2