├── README.md ├── checkrestart ├── Changelog ├── README.md └── checkrestart.octopuce ├── facts2sshfp ├── README.md └── facts2sshfp.py ├── ipset ├── 30_ipset.sh ├── 30_ipset_off.sh ├── 30_ipset_v6.sh ├── README.md └── blacklist.sh ├── letsencrypt-renew ├── README.md └── letsencrypt-auto-renew.sh ├── octobackup ├── README.md └── backup.sh ├── octopuce.png ├── sata ├── spamassin-split ├── sa-split.py └── scoreMinGen.py └── supervisor └── supervisor-inotify.sh /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![](https://github.com/octopuce/octopuce-goodies/blob/master/octopuce.png) 3 | 4 | 5 | Octopuce Goodies 6 | ================ 7 | 8 | Ce dépôt contient des scripts publiés par Octopuce sur son blog 9 | à l'adresse https://www.octopuce.fr/categorie/blog/ (en français) 10 | 11 | Chaque utilitaire pour administrateur système est décrit dans une page de blog dédiée. 12 | 13 | Patchs & améliorations bienvenues 14 | 15 | Scripts distribués sous licence CC0 sauf indication contraire. 16 | 17 | ---- 18 | 19 | Those are misc. scripts published by Octopuce on its blog 20 | at https://www.octopuce.fr/categorie/blog/ (mostly in French) 21 | 22 | Those sysadmin utilites are described in a blog page explaining what they do. 23 | 24 | Pull requests are welcomed. 25 | 26 | Everything here is distributed under CC0 license unless stated otherwise. 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /checkrestart/Changelog: -------------------------------------------------------------------------------- 1 | 2 | 20150128 added spamassassin and mailgraph daemons by Maethor 3 | added postgresql 8.4 & 9.1 (and other versions) by Benjamin Sonntag 4 | 5 | 20150126 First released version of checkrestart.octopuce 6 | 7 | -------------------------------------------------------------------------------- /checkrestart/README.md: -------------------------------------------------------------------------------- 1 | 2 | Script de redémarrage de services pour Octopuce 3 | =============================================== 4 | 5 | voir https://www.octopuce.fr/checkrestart-outil-pratique-de-debian-goodies-et-version-octopuce/ 6 | 7 | -------------------------------------------------------------------------------- /checkrestart/checkrestart.octopuce: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # vim:set sw=4 ts=4 sts=4 expandtab: 3 | # wraps checkrestart found in debian-goodies package 4 | # Adds : 5 | # logging in /var/log/checkrestart.log 6 | # Automatic restart of services found 7 | # Automatic restart of special cases, ex: munin, postgres 8 | # Manual user restart for the other packages 9 | 10 | TMPFILE=/tmp/checkrestart.$$.log 11 | TMPFILE2=/tmp/checkrestart.part2.$$.log 12 | LOGFILE=/var/log/checkrestart.log 13 | 14 | echo "#### Launching checkrestart..." 15 | checkrestart >$TMPFILE 16 | echo "#### Managing automatic cases" 17 | if grep -q "service .* restart" $TMPFILE 18 | then 19 | echo "#### I will automatically restart the following services : " 20 | grep "service .* restart" $TMPFILE | grep -v "service lxc restart" | grep -v "service xinetd restart" | sed -e 's/^service //' -e 's/ restart//' | tr '\n' ' ' 21 | echo 22 | echo -n "#### Please confirm [Yn] " ; read CONFIRM 23 | if [ "$CONFIRM" != "n" -a "$CONFIRM" != "N" ] 24 | then 25 | if grep -q "service xinet restart" $TMPFILE 26 | then 27 | echo "#### xinetd must be stop/start, doing now..." 28 | service xinetd stop 29 | sleep 1 30 | service xinetd start 31 | fi 32 | grep "service .* restart" $TMPFILE | bash 33 | echo "#### DONE" 34 | echo "" 35 | # After we did this, we should NOT have any service restart anymore ! 36 | checkrestart >$TMPFILE 37 | if grep -q "service .* restart" $TMPFILE 38 | then 39 | echo "#### We STILL HAVE services to reboot, please check them manually : " 40 | grep "service .* restart" $TMPFILE 41 | fi 42 | fi 43 | else 44 | echo "#### none" 45 | fi 46 | 47 | grep -A 1000 "These processes do not seem to have an associated init script to restart them:" $TMPFILE |grep -v "These processes do not seem to have an associated init script to restart them:" >$TMPFILE2 48 | 49 | echo "#### now managing special cases:" 50 | DONESPECIFIC=0 51 | 52 | # LOGIN 53 | if grep -q "/bin/login$" $TMPFILE2 54 | then 55 | echo "#### Killing login: " 56 | for pid in $(grep "/bin/login$" $TMPFILE2|awk '{print $1}') 57 | do 58 | echo "killing $pid" 59 | kill -9 $pid 60 | DONESPECIFIC=1 61 | done 62 | fi 63 | 64 | # PROSODY 65 | if grep -q "/usr/bin/lua5.1$" $TMPFILE2 66 | then 67 | for pid in $(grep "/usr/bin/lua5.1$" $TMPFILE2|awk '{print $1}') 68 | do 69 | # lua launched as prosody = prosody server 70 | if [ "$(ps auxwww|grep "$pid"|grep "^prosody")" ] 71 | then 72 | echo "#### restarting prosody having pid $pid: " 73 | service prosody restart 74 | DONESPECIFIC=1 75 | fi 76 | done 77 | fi 78 | 79 | 80 | # AMAVIS & MUNIN-NODE & SYMPA 81 | if grep -q "/usr/bin/perl$" $TMPFILE2 82 | then 83 | RESTARTMAILGRAPH=0 84 | mailgraphpids="" 85 | RESTARTSPAMD=0 86 | spamdpids="" 87 | RESTARTAMAVIS=0 88 | amavispids="" 89 | RESTARTMUNINNODE=0 90 | muninpids="" 91 | RESTARTSYMPA=0 92 | sympapids="" 93 | for pid in $(grep "/usr/bin/perl$" $TMPFILE2|awk '{print $1}') 94 | do 95 | # perl launched as amavis = amavisd 96 | if [ "$(ps auxwww|grep "$pid"|grep "mailgraph$")" ] 97 | then 98 | RESTARTMAILGRAPH=1 99 | mailgraphpids="$pids $pid" 100 | fi 101 | if [ "$(ps auxwww|grep "$pid"|grep "spamd")" ] 102 | then 103 | RESTARTSPAMD=1 104 | spamdpids="$pids $pid" 105 | fi 106 | if [ "$(ps auxwww|grep "$pid"|grep "^amavis")" ] 107 | then 108 | RESTARTAMAVIS=1 109 | amavispids="$pids $pid" 110 | fi 111 | if [ "$(ps auxwww|grep "$pid"|grep "munin-node$")" ] 112 | then 113 | RESTARTMUNINNODE=1 114 | muninpids="$pid" 115 | fi 116 | if [ "$(ps auxwww|grep "$pid"|grep "^sympa")" ] 117 | then 118 | RESTARTSYMPA=1 119 | sympapids="$pids $pid" 120 | fi 121 | done 122 | if [ "$RESTARTMAILGRAPH" -eq "1" ] 123 | then 124 | echo "#### restarting mailgraph having pids $mailgraphpids: " 125 | service mailgraph restart 126 | DONESPECIFIC=1 127 | fi 128 | if [ "$RESTARTSPAMD" -eq "1" ] 129 | then 130 | echo "#### restarting spamassassin having pids $spamdpids: " 131 | service spamassassin restart 132 | DONESPECIFIC=1 133 | fi 134 | if [ "$RESTARTAMAVIS" -eq "1" ] 135 | then 136 | echo "#### restarting amavis having pids $amavispids: " 137 | service amavis restart 138 | DONESPECIFIC=1 139 | fi 140 | if [ "$RESTARTMUNINNODE" -eq "1" ] 141 | then 142 | echo "#### restarting munin-node having pids $muninpids: " 143 | service munin-node restart 144 | DONESPECIFIC=1 145 | fi 146 | if [ "$RESTARTSYMPA" -eq "1" ] 147 | then 148 | echo "#### restarting sympa having pids $sympapids: " 149 | service sympa restart 150 | DONESPECIFIC=1 151 | fi 152 | fi 153 | 154 | 155 | 156 | # DOVECOT IMAPD 157 | if grep -q "/usr/lib/dovecot/imap$" $TMPFILE2 158 | then 159 | pid=$(grep "/usr/lib/dovecot/imap$" $TMPFILE2 |awk '{print $1}' | tr '\n' ' ') 160 | echo "#### restarting dovecot having pid $pid ... PLEASE CHECK DOVECOT AFTER THAT " 161 | service dovecot stop 162 | sleep 5 163 | kill -15 $(ps fauxw|grep "dovec[o]t/"|awk '{print $2}') 164 | sleep 5 165 | kill -9 $(ps fauxw|grep "dovec[o]t/"|awk '{print $2}') 166 | service dovecot start 167 | DONESPECIFIC=1 168 | fi 169 | 170 | 171 | # MYSQLD 172 | if grep -q "/usr/sbin/mysqld$" $TMPFILE2 173 | then 174 | pid=$(grep "/usr/sbin/mysqld$" $TMPFILE2 |awk '{print $1}' | tr '\n' ' ') 175 | echo "#### restarting mysqld having pid $pid" 176 | service mysql restart 177 | DONESPECIFIC=1 178 | fi 179 | 180 | # POSTGRESQL 8.4, 9.1 etc. 181 | if grep -q "/usr/lib/postgresql/[^/]*/bin/postgres$" $TMPFILE2 182 | then 183 | pid=$(grep "/usr/lib/postgresql/[^/]*/bin/postgres$" $TMPFILE2 |awk '{print $1}' | tr '\n' ' ') 184 | echo "#### restarting postgresql having pid $pid" 185 | service postgresql restart 186 | DONESPECIFIC=1 187 | fi 188 | 189 | # GAM_SERVER (used by fail2ban) 190 | if grep -q "/usr/lib/gamin/gam_server$" $TMPFILE2 191 | then 192 | pid=$(grep "/usr/lib/gamin/gam_server$" $TMPFILE2 |awk '{print $1}' | tr '\n' ' ') 193 | echo "#### killing gamin gam_server having pid $pid" 194 | kill $pid 195 | DONESPECIFIC=1 196 | fi 197 | 198 | # PYTHON2.7 : fail2ban server 199 | if grep -q "/usr/bin/python2.7$" $TMPFILE2 200 | then 201 | FAIL2BANFOUND=0 202 | FAIL2BANPID="" 203 | MAILMANFOUND=0 204 | for pid in $(grep "/usr/bin/python2.7$" $TMPFILE2|awk '{print $1}') 205 | do 206 | # python launched as /usr/bin/fail2ban-server 207 | # root 2144 0.0 0.0 71888 7900 ? Sl juil.18 10:13 /usr/bin/python /usr/bin/fail2ban-server -b -s /var/run/fail2ban/fail2ban.sock 208 | 209 | if [ "$(ps auxwww|grep "$pid"|grep "/usr/bin/fail2ban-server")" ] 210 | then 211 | FAIL2BANFOUND=1 212 | FAIL2BANPID="$pids $pid" 213 | fi 214 | 215 | if [ "$(ps auxwww|grep "$pid"|grep "^list")" ] 216 | then 217 | MAILMANFOUND=1 218 | fi 219 | done 220 | if [ "$FAIL2BANFOUND" -eq "1" ] 221 | then 222 | echo "#### restarting fail2ban having pids $FAIL2BANPID: " 223 | service fail2ban restart 224 | DONESPECIFIC=1 225 | fi 226 | if [ "$MAILMANFOUND" -eq "1" ] 227 | then 228 | echo "#### restarting mailman: " 229 | service mailman restart 230 | DONESPECIFIC=1 231 | fi 232 | fi 233 | 234 | 235 | 236 | 237 | 238 | # End of specific process management: 239 | if [ "$DONESPECIFIC" != "0" ] 240 | then 241 | echo "#### Finished" 242 | checkrestart >$TMPFILE 243 | else 244 | echo "#### None done" 245 | fi 246 | 247 | 248 | echo "#### please fix the remaining manually : " 249 | cat $TMPFILE 250 | 251 | echo "#### now logging into $LOGFILE" 252 | echo "################################################################################" >>$LOGFILE 253 | echo "# checkrestart launched on $(date)" >>$LOGFILE 254 | cat $TMPFILE >>$LOGFILE 255 | rm -f $TMPFILE2 $TMPFILE 256 | 257 | -------------------------------------------------------------------------------- /facts2sshfp/README.md: -------------------------------------------------------------------------------- 1 | 2 | Script de convertion des empreinte SSH vers des entrées DNS SSHFP 3 | ================================================================= 4 | 5 | voir https://www.octopuce.fr/declaration-automatique-des-cles-ssh-dans-le-dns-via-sshfp/ 6 | 7 | -------------------------------------------------------------------------------- /facts2sshfp/facts2sshfp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #(@)facts2sshfp.py (C) February 2012, by Jan-Piet Mens 3 | # (C) August 2014, by benjamin Sonntag for (non-mandatory) ECDSA keys 4 | # with inspiration from Pieter Lexis 5 | # 6 | # Slurp through the YAML fact files collected on a Puppet master, 7 | # extract the SSH RSA/DSA public keys and produce SSHFP records for DNS. 8 | # See also: http://jpmens.net/2011/07/21/it-s-a-fact-in-puppet/ 9 | # and: http://jpmens.net/2012/02/01/on-collecting-ssh-host-keys-for-sshfp-dns-records/ 10 | # 11 | # This program contains create_sshfp, which I swiped from Paul Wouter's SSHFP at 12 | # http://www.xelerance.com/services/software/sshfp/. I have removed the hostname 13 | # magic, because not needed here, as we know the fqdn (hopefully) 14 | 15 | 16 | import glob 17 | import sys, re 18 | import yaml 19 | import json 20 | import codecs 21 | import base64 22 | try: 23 | import hashlib 24 | digest = hashlib.sha1 25 | except ImportError: 26 | import sha 27 | digest = sha.new 28 | import string 29 | from string import Template 30 | import optparse 31 | 32 | factsdir = '/var/lib/puppet/yaml/facts' 33 | 34 | def create_sshfp(hostname, keytype, keyblob): 35 | """Creates an SSH fingerprint""" 36 | 37 | if keytype == "ssh-rsa": 38 | keytype = "1" 39 | else: 40 | if keytype == "ssh-dss": 41 | keytype = "2" 42 | else: 43 | if keytype == "ssh-ecdsa": 44 | keytype = "3" 45 | else: 46 | return "" 47 | try: 48 | rawkey = base64.b64decode(keyblob) 49 | except TypeError: 50 | print >> sys.stderr, "FAILED on hostname "+hostname+" with keyblob "+keyblob 51 | return "ERROR" 52 | 53 | fpsha1 = digest(rawkey).hexdigest().upper() 54 | 55 | # return hostname + " IN SSHFP " + keytype + " 1 " + fpsha1 56 | return { 57 | "keytype" : keytype, 58 | "fpsha1" : fpsha1 59 | } 60 | 61 | def facts_to_dict(filename): 62 | """ 63 | Return dict from YAML contained in filename, having removed the 64 | fugly Ruby constructs added by Puppet. 65 | (e.g. --- !ruby/object:Puppet::Node::Facts) which no YAML 66 | """ 67 | 68 | searchregex = "!ruby.*\s?" 69 | cregex = re.compile(searchregex) 70 | 71 | # "--- !ruby/sym _timestamp": Thu Jan 12 12:25:02 +0100 2012 72 | # !ruby/sym _timestamp: Tue Oct 04 07:56:54 +0200 2011 73 | rubyre = '"?(--- )?!ruby/sym ([^"]+)"?:\s+(.*)$' 74 | nrub = re.compile(rubyre) 75 | 76 | 77 | stream = '' 78 | for line in open(filename, 'r'): 79 | 80 | # Pieter reports his fact files contain e.g. ec2_userdata with binary blobs in them 81 | # Remove all that binary stuff.... 82 | 83 | line = filter(lambda x: x in string.printable, line) 84 | if nrub.search(line): 85 | line = nrub.sub(r'\2: \3', line) 86 | if cregex.search(line): 87 | line = cregex.sub("\n", line) 88 | stream = stream + line 89 | 90 | yml = yaml.load(stream) 91 | return yml['values'] 92 | 93 | if __name__ == '__main__': 94 | 95 | naming = 'fqdn' 96 | domainname = '' 97 | template = '' 98 | keylist = [] 99 | 100 | parser = optparse.OptionParser() 101 | parser.add_option('-d', '--directory', dest='factsdir', help='Directory containing facts') 102 | parser.add_option('-H', '--hostname', dest='usehost', default=False, help='Use hostname i/o fqdn', 103 | action='store_true') 104 | parser.add_option('-D', '--domainname', dest='domainname', help='Append domain') 105 | parser.add_option('-Q', '--qualify', dest='qualify', default=False, help='Qualify hostname with dot', action='store_true') 106 | parser.add_option('-J', '--json', dest='jsonprint', default=False, help='Print JSON', action='store_true') 107 | parser.add_option('-Y', '--yaml', dest='yamlprint', default=False, help='Print YAML', action='store_true') 108 | parser.add_option('-T', '--template', dest='templatename', help='Print using template file') 109 | 110 | 111 | (opts, args) = parser.parse_args() 112 | 113 | if opts.factsdir: 114 | factsdir = opts.factsdir 115 | if opts.usehost: 116 | naming = 'hostname' 117 | if opts.domainname: 118 | domainname = opts.domainname 119 | if opts.templatename: 120 | template = open(opts.templatename, 'r').read() 121 | 122 | for filename in glob.glob(factsdir + "/*.yaml"): 123 | facts = facts_to_dict(filename) 124 | 125 | item = {} 126 | rsa = create_sshfp(facts[naming], 'ssh-rsa', facts['sshrsakey']) 127 | if 'sshdsakey' in facts: 128 | dsa = create_sshfp(facts[naming], 'ssh-dss', facts['sshdsakey']) 129 | else: 130 | dsa = 0 131 | if 'sshecdsakey' in facts: 132 | ecdsa = create_sshfp(facts[naming], 'ssh-ecdsa', facts['sshecdsakey']) 133 | else: 134 | ecdsa = 0 135 | 136 | item['hostname'] = facts['hostname'] 137 | item['fqdn'] = facts['fqdn'] 138 | if domainname != '': 139 | item['domain'] = domainname 140 | else: 141 | item['domain'] = facts['domain'] 142 | 143 | if naming == 'hostname': 144 | owner = item['hostname'] 145 | else: 146 | owner = item['hostname'] + '.' + item['domain'] 147 | if opts.qualify == True: 148 | owner = owner + '.' 149 | item['owner'] = owner 150 | 151 | item['rsa_fp'] = rsa['fpsha1'] 152 | item['rsa_keytype'] = rsa['keytype'] 153 | if dsa: 154 | item['dsa_fp'] = dsa['fpsha1'] 155 | item['dsa_keytype'] = dsa['keytype'] 156 | if ecdsa: 157 | item['ecdsa_fp'] = ecdsa['fpsha1'] 158 | item['ecdsa_keytype'] = ecdsa['keytype'] 159 | 160 | keylist.append(item) 161 | 162 | if opts.jsonprint == True: 163 | print json.dumps(keylist, indent=4) 164 | elif opts.yamlprint == True: 165 | print yaml.dump(keylist, default_flow_style=False, explicit_start=True) 166 | elif opts.templatename: 167 | for item in keylist: 168 | s = Template(template) 169 | print s.substitute(item) 170 | else: 171 | for item in keylist: 172 | print "%-20s IN SSHFP %s 1 %s" % (item['owner'], item['rsa_keytype'], item['rsa_fp']) 173 | print "%-20s IN SSHFP %s 1 %s" % (item['owner'], item['dsa_keytype'], item['dsa_fp']) 174 | if 'ecdsa_fp' in item: 175 | print "%-20s IN SSHFP %s 1 %s" % (item['owner'], item['ecdsa_keytype'], item['ecdsa_fp']) 176 | 177 | -------------------------------------------------------------------------------- /ipset/30_ipset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # "destroy" may fail if iptables not properly flushed before 4 | 5 | ipset flush blackip4 6 | ipset flush blacknet4 7 | 8 | ipset destroy blackip4 9 | ipset destroy blacknet4 10 | 11 | ipset create blackip4 hash:ip family inet timeout 14400 maxelem 131072 12 | ipset create blacknet4 hash:net family inet timeout 14400 maxelem 65536 13 | 14 | iptables -F fipset 2>/dev/null 15 | iptables -X fipset 2>/dev/null 16 | 17 | iptables -N fipset 18 | # WHITELIST IPSET: 19 | iptables -A fipset -s 91.194.60.0/23 -j RETURN # ipv4 main pi 20 | iptables -A fipset -s 193.56.58.0/24 -j RETURN # ipv4 cursys pi 21 | iptables -A fipset -s 185.34.32.0/22 -j RETURN # ipv4 pa 22 | iptables -A fipset -s 212.83.165.226 -j RETURN # benedict 23 | iptables -A fipset -s 173.230.154.187 -j RETURN # secondary 24 | 25 | iptables -A fipset -m set --match-set blackip4 src -j REJECT 26 | iptables -A fipset -m set --match-set blacknet4 src -j REJECT 27 | 28 | iptables -A FORWARD -j fipset 29 | 30 | -------------------------------------------------------------------------------- /ipset/30_ipset_off.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # flush & destroy ipsets: 4 | ipset flush blackip4 5 | # this next one may fail if iptables not properly flushed before 6 | ipset destroy blackip4 7 | 8 | ipset flush blackip6 9 | # this next one may fail if iptables not properly flushed before 10 | ipset destroy blackip6 11 | -------------------------------------------------------------------------------- /ipset/30_ipset_v6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # "destroy" may fail if iptables not properly flushed before 4 | 5 | ipset flush blacknet6 6 | 7 | ipset destroy blacknet6 8 | 9 | ipset create blacknet6 hash:net family inet6 timeout 14400 maxelem 131072 10 | 11 | ip6tables -F fipset 2>/dev/null 12 | ip6tables -X fipset 2>/dev/null 13 | 14 | ip6tables -N fipset 15 | # WHITELIST IPSET: 16 | ip6tables -A fipset -s 2001:67c:288::/48 -j RETURN # ipv6 main pi 17 | ip6tables -A fipset -s 2a00:99a0::/32 -j RETURN # ipv6 pa 18 | ip6tables -A fipset -s 2600:3c01:e000:1c::1 -j RETURN # secondary 19 | 20 | ip6tables -A fipset -m set --match-set blackip6 src -j REJECT 21 | ip6tables -A fipset -m set --match-set blacknet6 src -j REJECT 22 | 23 | ip6tables -A FORWARD -j fipset 24 | 25 | -------------------------------------------------------------------------------- /ipset/README.md: -------------------------------------------------------------------------------- 1 | 2 | Liste noire IPSET pour Octopuce 3 | =============================== 4 | 5 | Voir https://www.octopuce.fr/ipset-filtrages-des-attaques-sur-les-serveurs/ 6 | -------------------------------------------------------------------------------- /ipset/blacklist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! "$1" ] 4 | then 5 | echo "Manages a blacklist of IPV4 addresses or network, and IPV6 networks" 6 | echo "Usage: " 7 | echo " cat iplist|blacklist add - add IP or network to the blacklist" 8 | echo " cat iplist|blacklist del - removes IP from the blacklist" 9 | echo " blacklist list - list all ip/net in the blacklist" 10 | echo " blacklist flush - purge all blacklisting on this machine" 11 | echo " blacklist add and del can take an ip or net as second parameter, " 12 | echo " in that case, stdin is not used" 13 | echo 14 | exit 15 | fi 16 | 17 | if [ "$1" = "flush" ] 18 | then 19 | ipset flush blackip4 20 | ipset flush blacknet4 21 | ipset flush blacknet6 22 | echo "Flushed" 23 | exit 24 | fi 25 | 26 | if [ "$1" = "list" ] 27 | then 28 | ipset list 29 | fi 30 | 31 | function ips { 32 | OPERATION="$1" 33 | IP="$2" 34 | if echo "$IP" | grep -q ":" 35 | then 36 | # IPV6 NET 37 | ipset "$OPERATION" blacknet6 "$IP" 2>/dev/null 38 | else 39 | # IPV4, net or ip ? 40 | if echo "$IP" | grep -q "/" 41 | then 42 | # IPV4 NET 43 | ipset "$OPERATION" blacknet4 "$IP" 2>/dev/null 44 | else 45 | # IPV4 IP 46 | ipset "$OPERATION" blackip4 "$IP" 2>/dev/null 47 | fi 48 | fi 49 | if [ "$?" = "0" ] 50 | then 51 | echo "$OPERATION $IP OK" 52 | else 53 | echo "ERROR $OPERATION $IP" 54 | fi 55 | } 56 | 57 | 58 | if [ "$1" = "add" ] 59 | then 60 | if [ "$2" != "" ] 61 | then 62 | ips add "$2" 63 | else 64 | while read IP 65 | do 66 | ips add "$IP" 67 | done 68 | fi 69 | fi 70 | 71 | 72 | if [ "$1" = "del" ] 73 | then 74 | if [ "$2" != "" ] 75 | then 76 | ips del "$2" 77 | else 78 | while read IP 79 | do 80 | ips del "$IP" 81 | done 82 | fi 83 | fi 84 | -------------------------------------------------------------------------------- /letsencrypt-renew/README.md: -------------------------------------------------------------------------------- 1 | # Script de renouvellement des certificats letsencrypt 2 | 3 | Ce script renouvelle automatiquement les certficats LetsEncrypt pointés par des liens symboliques dans /etc/letsencrypt/live, et ce 30 jours avant leur date d'expiration 4 | 5 | Ce script a été publié dans le cadre d'un article pour le magazine MiscMag : http://www.miscmag.com/ 6 | 7 | -------------------------------------------------------------------------------- /letsencrypt-renew/letsencrypt-auto-renew.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Send alert mail to this address: 4 | ALERT=sslalert@octopuce.fr 5 | LETSENCRYPT_BIN=/usr/local/letsencrypt/letsencrypt-auto 6 | WEBROOT=/var/www/letsencrypt 7 | 8 | cd "$(dirname $0)" 9 | HERE="$PWD" 10 | DATE_TODAY=$(date +'%s') 11 | cd /etc/letsencrypt/live || (echo "Can't cd to /etc/letsencrypt/live !" && exit -1) 12 | for domain in * 13 | do 14 | if [ -f "$domain/cert.pem" ] ; then 15 | CERT="$domain/cert.pem" 16 | CERT_END_DATE=$(openssl x509 -in "$CERT" -noout -enddate | sed -e "s/.*=//") 17 | DATE_CERT=$(date -ud "$CERT_END_DATE" +"%s") 18 | DATE_JOURS_DIFF=$(( ( $DATE_CERT - $DATE_TODAY ) / (60*60*24) )) 19 | if [[ $DATE_JOURS_DIFF -le 30 ]]; then 20 | echo "Trying to renew certificate for domain $domain expiring in $DATE_JOURS_DIFF days" 21 | # Read the SAN (Subject Alt Names) for this cert (Warn: this code may not be super reliable :/ ) 22 | SAN=$(openssl x509 -in "$CERT" -text|grep DNS:|sed -e "s/DNS:/-d /g" -e "s/, / /g") 23 | # Try to renew it: 24 | $LETSENCRYPT_BIN certonly --force-renewal --webroot -w "$WEBROOT" -d "$domain" $SAN 25 | if [ "$?" -ne "0" ] 26 | then 27 | echo "Certificate /etc/letsencrypt/live/$domain has NOT been successfully renewed, please check" | mail -s "Can't renew certificate $domain on $(hostname)" $ALERT 28 | else 29 | # TODO : auto-reload of services ? 30 | echo "Renewed certificate $domain on $(hostname)" 31 | fi 32 | 33 | fi 34 | fi 35 | done 36 | 37 | -------------------------------------------------------------------------------- /octobackup/README.md: -------------------------------------------------------------------------------- 1 | # Script pour faire des sauvegardes facilement 2 | 3 | https://www.octopuce.fr/faire-des-backups-facilement/ 4 | 5 | ```bash 6 | wget https://github.com/octopuce/octopuce-goodies/blob/master/octobackup/backup.sh 7 | mv backup.sh /usr/local/bin 8 | chmod +x /usr/local/bin/backup.sh 9 | ``` 10 | 11 | Ne pas oublier de modifier DEST et HOST. 12 | 13 | -------------------------------------------------------------------------------- /octobackup/backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Paramètres du backup 4 | ## Vers quel disque dur 5 | DEST=/media/octobackup/ 6 | ## Nombre de jours au-delà duquel on supprime les anciens backups 7 | DAYS=300 8 | ## Nom de votre ordinateur (pratique si vous en avez plusieurs) 9 | HOST="laptop" 10 | 11 | RSYNC_OPTIONS="-P -H" 12 | if [ ! -d "$DEST" ] 13 | then 14 | echo "Disque de sauvegarde non monté !" 15 | exit 1 16 | fi 17 | # On backup dans un dossier _ 18 | NOW="${HOST}_`date +%Y%m%d`" 19 | LINKDEST="" 20 | for i in `ls -rt $DEST|tail -3` 21 | do 22 | if [ "$i" != "$NOW" ] 23 | then 24 | LINKDEST="$LINKDEST --link-dest $DEST/$i/" 25 | fi 26 | done 27 | 28 | mkdir "$DEST/$NOW" 29 | rsync / $DEST/$NOW/ -a --delete --numeric-ids --exclude "proc/*" --exclude "sys/*" --exclude "dev/*" --exclude "home/skhaen/.gvfs" --exclude "media/*" --exclude "/mnt/" $LINKDEST $RSYNC_OPTIONS 30 | touch "$DEST/$NOW" 31 | 32 | # Ensuite on fait du ménage : effacement des dossiers de plus de X jours 33 | echo "Cleanup ..." 34 | find "$DEST" -maxdepth 1 -mindepth 1 -type d -mtime +$DAYS -print -exec rm -rf {} \; 35 | -------------------------------------------------------------------------------- /octopuce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopuce/octopuce-goodies/ec03047126601fd19fdd61083a4502714b12061f/octopuce.png -------------------------------------------------------------------------------- /sata: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #============================================================================================# 3 | # Copyright (C) 2014 4 | # Author: Alban 5 | # 6 | # Description 7 | # ----------- 8 | # This script provides functionality for SATA disks on Linux system : 9 | # - remove disk 10 | # - scan host for new disks 11 | # 12 | #============================================================================================# 13 | #============================================================================================# 14 | # SCRIPT SETTINGS 15 | #============================================================================================# 16 | ISROOTREQUIRED=1 # Use 0 if you want to run it with non-root users 17 | FORCEARGUMENTSREQUIRED=0 # Use 0 if you want to run it without arguments 18 | #============================================================================================# 19 | # Variables 20 | #============================================================================================# 21 | BN=${0##*/} 22 | MYPID=$(echo $$) 23 | #============================================================================================# 24 | # ERROR CODES 25 | #============================================================================================# 26 | ROOTISREQUIRED=100 27 | NOPARAMETERSPROVIDED=101 28 | CUSTOM_ERROR2=102 29 | PARAMISREQUIRED=103 30 | PARAMSDX=104 31 | HOSTDIR=105 32 | HOSTDIR=106 33 | INVALIDMD=107 34 | INVALIDBD=108 35 | #============================================================================================# 36 | # COMMON FUNCTIONS 37 | #============================================================================================# 38 | exit_usage() 39 | { 40 | ERRCODE=$1 41 | MSG=$2 42 | if [[ -z $MSG ]] ; then 43 | cat <] 47 | DESCRIPTION 48 | scan Will scan all the SATA hosts to refresh disks 49 | remove Will remove one disk IF not used in RaidMD 50 | EXAMPLE 51 | ${BN} scan 52 | ${BN} remove $BLOCK_DEVICE 53 | StartHere 54 | else 55 | echo $MSG 56 | fi 57 | exit $ERRCODE 58 | } # end exit_usage() 59 | 60 | isRoot() # This method check if the current user is root 61 | { 62 | if [ "root" != "$( whoami )" ] && [ $ISROOTREQUIRED -eq 1 ] ; then 63 | echo "${BN} must be run as root" 64 | exit_usage $ROOTISREQUIRED 65 | fi 66 | } 67 | 68 | 69 | #============================================================================================# 70 | # Main methods 71 | #============================================================================================# 72 | scan() 73 | { 74 | echo "Scanning host" 75 | for s in `find /sys/devices/ -name "*scsi_host"`; do 76 | echo "scanning $s" 77 | FILE=`find $s -name "scan"` 78 | echo "- - - " > $FILE 79 | done; 80 | } 81 | remove() 82 | { 83 | # Check parameter 84 | BLOCK_DEVICE="$1" 85 | REGEX="^sd[[:lower:]]$" 86 | 87 | if [ -z "$BLOCK_DEVICE" ] ; then 88 | exit_usage $PARAMISREQUIRED "You must provide a valid disk name as parameter eg. sdX" 89 | fi 90 | if [[ ! $BLOCK_DEVICE =~ $REGEX ]] ; then 91 | exit_usage $PARAMSDX "Invalid device, please provide a 'sdX'" 92 | fi 93 | 94 | echo "Checking if block device exists..." 95 | SYS=(find /sys/block -name $BLOCK_DEVICE) 96 | if [ -z $SYS ] ; then exit_usage $INVALIDBD "You provided an invalid block device"; fi 97 | 98 | echo "Checking mdraid usage..." 99 | MD=$(grep $BLOCK_DEVICE[0-9] /proc/mdstat) 100 | if [ 0 = $? ] ; then exit_usage $INVALIDMD "You provided o block device still used in MD array"; fi 101 | 102 | echo "Finding block device host..." 103 | HOST_DIR=`cd $(dirname /sys/block/$BLOCK_DEVICE) && cd $(readlink /sys/block/$BLOCK_DEVICE)/../../../../ && pwd` 104 | if [ ! -d $HOST_DIR ] ; then exit_usage $HOSTDIR "Could not find host directory for $BLOCK_DEVICE"; fi 105 | DELETE=`find $HOST_DIR -name delete` 106 | if [ -z $DELETE ] ; then exit_usage $HOSTDELETE "Could not find host delete access for $BLOCK_DEVICE"; fi 107 | echo $DELETE 108 | #echo "1" > $DELETE 109 | 110 | 111 | } 112 | 113 | 114 | #============================================================================================# 115 | # Startup code (This section contains logic that occurs before processing the arguments) 116 | #============================================================================================# 117 | # Checking if the script was executed as root (it may disabled by setting ISROOTREQUIRED as 0) 118 | isRoot 119 | # Check if arguments are set (it may disabled by setting FORCEARGUMENTSREQUIRED as 0) 120 | if [ $# -eq 0 ] && [ $FORCEARGUMENTSREQUIRED -eq 1 ] 121 | then 122 | echo "You need to provide parameters" 123 | exit_usage $NOPARAMETERSPROVIDED 124 | fi 125 | #============================================================================================# 126 | # Script argument processing 127 | #============================================================================================# 128 | 129 | ACTION=$1 130 | case $ACTION in 131 | (scan) scan;; 132 | (remove) remove "$2";; 133 | (*) exit_usage;; 134 | esac 135 | 136 | 137 | 138 | ### 139 | # Finish Bash script with Error CODE 0 = Everything is normal 140 | ### 141 | exit 0 142 | -------------------------------------------------------------------------------- /spamassin-split/sa-split.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys; 3 | import re; 4 | import os; 5 | import csv; 6 | import argparse 7 | from functools import reduce 8 | # Parsing 9 | parser = argparse.ArgumentParser(description="Extract and Simulate SpamAssassin scores") 10 | group = parser.add_mutually_exclusive_group(required=True) 11 | group.add_argument('-e', '--extract', action='store_true',help='Extracts scores from an EML file' ) 12 | group.add_argument('-s', '--simulate', action='store_true', help='Compares score from an EML file and a new scoring table') 13 | group.add_argument('-c', '--chart', action='store_true',help='Add existing scores to a CSV file for modification' ) 14 | parser.add_argument('-f', '--file', action='store', help='EML source file',required=True) 15 | parser.add_argument('-r', '--reference', action='store', help='Reference CSV scores file') 16 | parser.add_argument('-b', '--bar', action='store', help='Spam limit (>= means spam)') 17 | parser.add_argument('-v', '--verbose', action='store_true', help='Increase verbosity') 18 | args = parser.parse_args() 19 | class bcolors: 20 | FAIL = '\033[31m' 21 | ENDC = '\033[0m' 22 | 23 | # Main class 24 | class Scores: 25 | 26 | def getCompareDicts( self, refsFilename, sourceFilename ): 27 | tuples = self.getSourcesScores( sourceFilename ) 28 | fileDict = dict((key, value) for (key, value) in tuples) 29 | 30 | handle = open(refsFilename, 'a+') 31 | reader = csv.reader( handle, dialect='excel') 32 | refDict = dict((key, value) for (key, value) in reader) 33 | 34 | return [ fileDict, refDict ] 35 | 36 | def compare( self, refsFilename, sourceFilename, bar = 7.0): 37 | fileDict, refDict = self.getCompareDicts( refsFilename, sourceFilename ) 38 | fileSum = self.getSum( fileDict ) 39 | simulSum = 0.0 40 | for key in fileDict: 41 | if not( key in refDict ): 42 | print "woah. No " + key + " in reference..." 43 | continue 44 | simulSum += float( refDict[key]) 45 | color = bcolors.FAIL if simulSum >= bar else bcolors.ENDC 46 | print(color + "{0:.40s} before:{1:.1f} after:{2:.1f}".format(sourceFilename,fileSum,simulSum)+bcolors.ENDC) 47 | 48 | 49 | def setChartCsv( self, refsFilename, sourceFilename): 50 | 51 | fileDict, refDict = self.getCompareDicts( refsFilename, sourceFilename ) 52 | handle = open(refsFilename, 'a+') 53 | writer = csv.writer( handle, dialect='excel') 54 | for key in fileDict: 55 | if not( key in refDict ): 56 | refDict[key] = fileDict[key] 57 | writer.writerow( [ key.strip(), fileDict[key] ]) 58 | 59 | 60 | def getSourcesScores( self, sourceFilename ): 61 | output = [] 62 | if not ( os.path.isfile( sourceFilename ) ) : 63 | print "Not a file: '"+ sourceFilename +"'" 64 | exit(1) 65 | with open( sourceFilename ) as fileHandle : 66 | result = [] 67 | found = False 68 | linesList = fileHandle.readlines() 69 | for line in linesList: 70 | if found == False: 71 | if( re.match( "^X-Spam-Status", line ) ): 72 | found = True 73 | continue 74 | else: 75 | if not( re.match( "^\s", line ) ): 76 | break 77 | result.append(line) 78 | resultString = "".join( result ).replace("\r","").replace("\n","") 79 | scoresRegExp = re.match("^.*?\[(.*)\].*$",resultString) 80 | if( '' == resultString ): 81 | print "No tag in '"+sourceFilename+"'" 82 | exit(1) 83 | if( None == scoresRegExp ): 84 | print "Failed to parse '" + resultString +"' in file '"+ sourceFilename +"'" 85 | exit(1) 86 | scoresList = re.split(r',',scoresRegExp.groups()[0]) 87 | for score in scoresList: 88 | tuples = re.split( r'=',score.replace(' ','') ) 89 | tuples[0] = tuples[0].strip("\t ") 90 | output.append( tuples ) 91 | return output 92 | 93 | 94 | def getDestFilename( self, sourceFilename ): 95 | destDir = os.path.abspath(os.path.dirname(sourceFilename) + "/../csv") 96 | if not( os.path.isdir( destDir ) ): 97 | os.makedirs( destDir ) 98 | destFilename = destDir + "/" + os.path.basename(sourceFilename) + ".csv" 99 | return destFilename 100 | 101 | 102 | def writeCsv( self, sourceFilename, scoresList ): 103 | destFilename = self.getDestFilename( sourceFilename ) 104 | handle = open(destFilename, 'wb') 105 | writer = csv.writer( handle, dialect='excel') 106 | for tuples in scoresList : 107 | writer.writerow(tuples) 108 | 109 | 110 | def getScoresFromCsv( self, sourceFilename ): 111 | destFilename = self.getDestFilename( sourceFilename ) 112 | handle = open(destFilename, 'r') 113 | reader = csv.reader( handle, dialect='excel') 114 | return reader 115 | 116 | 117 | def getSum( self, scoresDict ): 118 | total = 0 119 | for name in scoresDict : 120 | total += float(scoresDict[ name ]) 121 | return total 122 | 123 | 124 | instance = Scores() 125 | sourceFilename = args.file 126 | if( args.extract ): 127 | scoresList = instance.getSourcesScores( sourceFilename ) 128 | destFilename = instance.getDestFilename( sourceFilename ) 129 | instance.writeCsv( destFilename, scoresList ) 130 | if args.verbose: print "Wrote to "+destFilename 131 | elif( args.chart ): 132 | referenceFilename = args.reference 133 | instance.setChartCsv( referenceFilename, sourceFilename ) 134 | else: 135 | referenceFilename = args.reference 136 | bar = args.bar if args.bar != None else 7.0 137 | instance.compare( referenceFilename, sourceFilename, float(bar)) 138 | 139 | 140 | -------------------------------------------------------------------------------- /spamassin-split/scoreMinGen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import sys; 3 | import os; 4 | 5 | 6 | 7 | def compareRuleFiles (sourceFile,modifiedFile): 8 | with open(sourceFile) as sf: 9 | source_data=sf.readlines() 10 | 11 | source_data=[item.strip() for item in source_data] 12 | sorted(source_data) 13 | with open(modifiedFile) as sf: 14 | modified_data=sf.readlines() 15 | 16 | modified_data=[item.strip() for item in modified_data] 17 | sorted(modified_data) 18 | 19 | for so_spam_lot in source_data : 20 | for mo_spam_lot in modified_data : 21 | if(so_spam_lot.split(",")[0]==mo_spam_lot.split(",")[0]): 22 | if(so_spam_lot.split(",")[1]!=mo_spam_lot.split(",")[1]): 23 | print("score " + mo_spam_lot.split(',')[0]+" "+mo_spam_lot.split(",")[1]) 24 | 25 | 26 | compareRuleFiles(sys.argv[1],sys.argv[2]) 27 | -------------------------------------------------------------------------------- /supervisor/supervisor-inotify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # auto-reload ARG[2] supervisor program when the ARG[1] path changes 3 | # Uses a callback system to avoid multiple executions 4 | # Licensed under Public Domain 5 | # 6 | # Mandatory for debian: apt-get install inotify-tools 7 | # 8 | # @author Alban Crommer 9 | 10 | 11 | ############################ 12 | # PARAMS 13 | ############################ 14 | PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.dotnet/tools" 15 | # Defines a delay for the callback command 16 | sleep_time=2 17 | 18 | 19 | ############################ 20 | # USAGE 21 | ############################ 22 | 23 | usage() { 24 | msg=" 25 | Rerun a supervisor command every time filesystem changes are detected. 26 | 27 | Usage: $(basename $0) [-h|--help] path supervisor_program 28 | 29 | -h, --help Display this help and exit. 30 | path The program installation path ex: /opt/myapp 31 | supervisor_program The program name as declared in supervisor ex: myapp 32 | " 33 | echo "$msg" 34 | } 35 | 36 | while [ $# -gt 0 ]; do 37 | case "$1" in 38 | -h|--help) usage; exit;; 39 | *) break;; 40 | esac 41 | shift 42 | done 43 | 44 | 45 | ############################ 46 | # FUNCTIONS / HELPERS 47 | ############################ 48 | 49 | # Function : timestamped log 50 | log(){ echo "`date -R ` $1"; } 51 | # Function : timestamp log + exit 52 | err(){ log "$@"; exit 1; } 53 | # Function : A callback executor with time based execution skip process based on event id 54 | # ARG[1] : The callback id 55 | # ARG[2] : The callback command 56 | # Executes the command unless another event preempted execution during the sleep 57 | callback(){ 58 | # Read from args 59 | local my_event_id="$1" 60 | local callback="$2" 61 | # Sleep 62 | sleep "$sleep_time"; 63 | # Read from file 64 | local last_event_id=$( cat $tmp_file) 65 | # Check for preemption 66 | if [[ "$my_event_id" != "$last_event_id" ]] ; then 67 | log "Callback $my_event_id Skipped: another event preempts me: '$last_event_id' " 68 | else 69 | log "Callback $my_event_id executes: '$callback'" 70 | $callback 71 | fi 72 | } 73 | 74 | 75 | ############################ 76 | # Args parsing 77 | ############################ 78 | 79 | # ARG[1] : app path 80 | [ -z "$1" ] && err "You MUST provide a path as argument" 81 | watch_path="$1" 82 | [[ ! -e "$watch_path" ]] && err "The request path is not valid : $watch_path" 83 | shift 84 | 85 | # ARG[2] : app name in supervisor 86 | [ -z "$1" ] && err "You MUST provide a program as argument" 87 | program="$1" 88 | supervisorctl avail | grep -q "^$program" || err "The requested program is not valid : $program" 89 | 90 | # Final tmp file based on program name 91 | tmp_file="/tmp/.mark.supervisor-$program" 92 | 93 | ############################ 94 | # Business logic starts here 95 | ############################ 96 | 97 | log "Started with path $watch_path for program $program" 98 | 99 | # Wait events forever 100 | 101 | while true; do 102 | 103 | # Receive event 104 | event_string=$(inotifywait --quiet --recursive --event create,delete,delete_self,modify,move --exclude 'logs' "$watch_path" ) 105 | log "Received event_id : $event_string" 106 | 107 | # Set event ID and store it 108 | event_id=$(date "+%Y-%m-%d_%H:%M:%S@%N"); 109 | echo "$event_id" > $tmp_file 110 | 111 | # Launch the callback and wait for a new event 112 | callback "$event_id" "supervisorctl restart $program" & 113 | 114 | done 115 | 116 | 117 | # EOF 118 | --------------------------------------------------------------------------------