# system: fedora, release: #based on "pc" configuration # Accepted kernel parameters: # part=root=4,vg=LVM_VG_NAME,swap=4,var=20,home=max # disks=vda,vdb # zerombr !!!USE WITH CAUTION!!! # lang=en_US # pkgs=desktop,salpack # config=http://boot.salstar.sk/ks/custom # text # # System authorization information and root password #auth --useshadow --enablemd5 rootpw --iscrypted $6$u5FhpPHs$N69Gq3rAUVCzyCHUiiH5fScNPPgB0qVAGhlJ52g5XE.R5BSepuH.BmwyMjOYnM9n3ew0cE0VIajJirBKY/Mzm0 # System bootloader configuration bootloader --append="" --location=mbr --timeout=1 # Firewall configuration (ssh is enabled by default) firewall --enabled # Run the Setup Agent on first boot firstboot --enable # System keyboard keyboard us # System language #lang sk_SK.UTF-8 #lang en_US.UTF-8 # Installation logging level #logging info # Network information network --bootproto=dhcp --onboot=on #vnc --host=158.197.240.41 --port=5500 # Reboot after installation reboot # Accept EULA eula --agreed # SELinux configuration selinux --permissive # System timezone timezone --utc Europe/Bratislava # Services #services --disabled=netfs --enabled=network # iSCSI #iscsiname iqn.2012-09.test #iscsi --ipaddr=158.197.16.70 --target=iqn.2012-09.sk.upjs.ftp:fedora # Disk partitioning information #part / --fstype="ext4" --grow --size=1 %packages --ignoremissing -@Base -@Base System dhclient openssh-clients openssh-server #screen wget rsync which iptables #joe #mc parted -subscription-manager -sendmail -timedatex %end %pre --interpreter /bin/bash # SAL's fedora/centos kickstart script if [ -x /usr/libexec/platform-python ]; then # CentOS 8 PYTHON=/usr/libexec/platform-python elif [ -x /usr/bin/python3 ]; then # Fedora 25+ has only python3 PYTHON=/usr/bin/python3 else PYTHON=/usr/bin/python fi $PYTHON << PYTHON_EOF from __future__ import print_function import sys, os, re, socket if sys.version_info[0]>2: from urllib.request import urlopen, HTTPError else: from urllib2 import urlopen, HTTPError part_names = dict( root='/', swap='swap', boot='/boot', efi='/boot/efi', var='/var', home='/home', tmp='/tmp', www='/var/www', backuppc='/var/lib/BackupPC', mysql='/var/lib/mysql', pgsql='/var/lib/pgsql', redis='/var/lib/redis', mongo='/var/lib/mongo', squid='/var/spool/squid', opt='/opt' ) sys.stdout = open('/tmp/salstar.ks', 'wt') spare = 2 # check distribution (el6 or fc20) distro, arch = os.popen('uname -r').read().strip().split('.')[-2:] distro = distro.split("_")[0] # remove minor release part if arch.endswith('86'): arch = "i386" # distribution variant (centos, almalinux, rocky) rh_release = open('/etc/redhat-release').read() if 'Fedora' in rh_release: distribution_variant = 'fedora' if 'AlmaLinux' in rh_release: distribution_variant = 'almalinux' elif 'Rocky' in rh_release: distribution_variant = 'rocky' else: distribution_variant = 'centos' # boot command line cmd_line = {} for cmd in open('/proc/cmdline').read().strip().split(' '): arg = None if '=' in cmd: print("# CMDLINE:", cmd) cmd, arg = cmd.split('=', 1) cmd_line[cmd] = arg def sys_block_read(hdx, name): fn = '/sys/block/%s/%s' % (hdx, name) if os.path.exists(fn): return open(fn).read().strip() return None def disksize(disks): ds = [ int(sys_block_read(x, 'size'))//2048 for x in disks ] print("# disksizes: %s = %s" % (disks, ds)) return min(ds) def size(s, disks=None, sum=0): if s in ['', 'max']: if disks is None: return "1 --grow" else: print("# sum=%d kB" % sum) return disksize(disks)-sum if s.endswith('m') or s.endswith('M'): return int(s[:-1]) else: return int(float(s.strip('gG'))*1024) class guess_fstype: def __init__(self, key, value): self.size = value self.arg = '' self.arg_lv = '' if ':' in value: self.fstype = value.split(':', 1)[1] self.size = value.split(':', 1)[0] elif key=='swap': self.fstype = '' elif key=='efi': self.fstype = 'efi' elif distro.startswith('fc'): self.fstype = "btrfs" else: self.fstype = "xfs" #self.fstype = "ext4" # store command argument self.is_btrfs = self.fstype=="btrfs" if self.fstype: self.arg = '--fstype="%s"' % self.fstype # Do not use btrfs for LVM volumes, broken in Fedora 35! if self.is_btrfs: self.arg_lv = '' else: self.arg_lv = self.arg class mk_raid: cntr = -1 def __init__(self, disks): self.disks = disks def add(self, s): raids = [] self.cntr += 1 for i in 0, 1: if self.disks[i]: args = "" if self.cntr==0: args += " --asprimary" print("part raid.%d%d --size=%s --ondisk=%s%s" % (self.cntr, i+1, s, self.disks[i], args)) raids.append("raid.%d%d" % (self.cntr, i+1)) return raids def add_raid1(self, name, s, fstype_arg=""): raids = self.add(s) print("raid %s %s --level=1 --device=md%d %s" % (name, fstype_arg, self.cntr, " ".join(raids))) def add_vg(self, vg, s): self.add_raid1("pv.01", s) print("volgroup %s pv.01" % vg) def add_part(self, mp, s, fstype_arg): self.add_raid1(mp, s, fstype_arg) def not_cdrom_or_lun(hd): try: media = sys_block_read(hd, 'device/media') if media=='cdrom': return False except IOError: pass try: model = sys_block_read(hd, 'device/model') if model=='LUNZ': return False except IOError: pass return True def detect_disks(disks=[]): if not disks: disks = [x for x in os.listdir('/sys/block') if (x.startswith('sd') or x.startswith('vd')) and not_cdrom_or_lun(x)] return [ x for x in disks if int(sys_block_read(x, 'size') or 0)>0 or x.startswith("disk/by-") ] def initdisks(disks): if 'zerombr' in cmd_line: print("zerombr") if disks: print("clearpart --all --initlabel --drives=%s" % ','.join([x for x in disks if x])) else: print("clearpart --all --initlabel") def autopartition(disks, parts): biosboot = 2 if distro.startswith('el7') or distro.startswith('el6'): biosboot = 0 if disks and disks.startswith('iscsi:'): print('iscsiname iqn.2012-09.test') print('iscsi --ipaddr=%s --target=%s --user=%s --password=%s' % tuple(disks[6:].split(';', 3))) return if not parts: return if disks: disks = detect_disks(re.split('[|,]', disks)) else: disks = detect_disks() print("# detected disks:", disks) if disks: print('ignoredisk --only-use=%s' % ','.join(disks)) parts = [x.split('=', 1) for x in parts.split(',')] counter = 100 sum = spare # 1 vg = None if len(disks)==1: # one disk partition scheme disk0 = disks[0] if parts: initdisks(disks) for key, value in parts: fs = guess_fstype(key, value) value = fs.size counter += 1 if key=='vg': vg = value print('part pv.%d --fstype="lvmpv" --ondisk=%s --size=1 --grow' % (counter, disk0)) print('volgroup %s pv.%d' % (vg, counter)) elif key=='biosboot': biosboot = size(value) print('part biosboot --fstype=biosboot --ondisk=%s --size=%d' % (disk0, biosboot)) sum += biosboot elif key in part_names: mountpoint = part_names[key] s = size(value, disks, sum) if fs.is_btrfs: # btrfs needs more settings if vg: # Fedora 35 btrfs on LVM is broken, use default instead #print('btrfs %s btrfs.%d' % (mountpoint, counter)) #print('logvol btrfs.%d %s --vgname="%s" --name="%s" --size=%s' # % (counter, fs.arg_lv, vg, key, s)) print('logvol %s %s --vgname="%s" --name="%s" --size=%s' % (mountpoint, fs.arg_lv, vg, key, s)) else: print('part btrfs.%d %s --ondisk=%s --size=%d' % (counter, fs.arg, disk0, s)) print('btrfs %s btrfs.%d' % (mountpoint, counter)) else: if vg: print('logvol %s %s --vgname="%s" --name="%s" --size=%s' % (mountpoint, fs.arg_lv, vg, key, s)) else: print('part %s %s --ondisk=%s --size=%d' % (mountpoint, fs.arg, disk0, s)) sum += s print('# sum=%d kB' % sum) elif len(disks)==2: # raid 1 partition scheme disk0 = disks[0] disk1 = disks[1] raid = mk_raid(disks) if parts: initdisks(disks) for key, value in parts: fs = guess_fstype(key, value) value = fs.size print('# key, value, fstype:', key, value, fs.arg) if key=='vg': vg = value # add pv # --grow disabled in F14 for raid partitions :-( #raid.add_vg(vg, '%d --grow' % s) print('# disk size: %d-%d' % (disksize(disks), sum)) raid.add_vg(vg, '%d' % (disksize(disks)-sum)) elif key=='biosboot': biosboot = size(value) print('part biosboot --fstype=biosboot --ondisk=%s --size=%d' % (disk0, biosboot)) print('part biosboot --fstype=biosboot --ondisk=%s --size=%d' % (disk1, biosboot)) sum += biosboot elif key in part_names: mountpoint = part_names[key] s = 0 if vg: print('logvol %s %s --vgname="%s" --name="%s" --size=%s' % (mountpoint, fs.arg_lv, vg, key, size(value))) elif fs.is_btrfs: s = size(value, disks, sum) raid.add_part('btrfs.%d' % counter, s, fs.arg) print('btrfs %s btrfs.%d' % (mountpoint, counter)) else: s = size(value, disks, sum) raid.add_part(mountpoint, s, fs.arg) sum += s print('# sum=%d kB' % sum) class repocfg_class: fc = dict( base = [ 'url --url=%(mirror)s/fedora/linux/%(dir)s/%(ver)d/Everything/%(arch)s/os/', 'repo --name=Updates --baseurl=%(mirror)s/fedora/linux/updates/%(ver)d/Everything/%(arch)s/' ], other = [ 'repo --name=Everything --baseurl=%(mirror)s/fedora/linux/%(dir)s/%(ver)d/Everything/%(arch)s/os/', 'repo --name=SALstar.sk --baseurl=https://www.salstar.sk/pub/fedora/%(ver)d/%(arch)s/' ], testing = [ 'repo --name=Updates-testing --baseurl=%(mirror)s/fedora/linux/updates/testing/%(ver)d/Everything/%(arch)s/' ] ) el = dict( base = [ 'url --url=%(mirror)s/%(variant)s/%(ver)d/os/%(arch)s/', 'repo --name=Updates --baseurl=%(mirror)s/%(variant)s/%(ver)d/updates/%(arch)s/', 'repo --name=EPEL --baseurl=%(mirror)s/mirrors/epel/%(ver)d/%(arch)s/' ], epel = [ 'repo --name=EPEL --baseurl=%(mirror)s/mirrors/epel/%(ver)d/%(arch)s/' ], cr = [ 'repo --name=CR --baseurl=%(mirror)/%(variant)s/%(ver)/cr/%(arch)/', ], other = [ 'repo --name=SALstar.sk --baseurl=https://www.salstar.sk/pub/epel/%(ver)d/%(arch)s/' ], testing = [ 'repo --name=EPEL-testing --baseurl=%(mirror)s/mirrors/epel/testing/%(ver)d/%(arch)s/' ] ) el8 = dict( base = [ 'url --url=%(mirror)s/%(variant)s/%(ver)d/BaseOS/%(arch)s/os/', #'repo --name=EPEL --baseurl=%(mirror)s/mirrors/epel/%(ver)d/Everything/%(arch)s/' ], epel = [ 'repo --name=EPEL --baseurl=%(mirror)s/mirrors/epel/%(ver)d/Everything/%(arch)s/' ], other = [ 'repo --name=SALstar.sk --baseurl=https://www.salstar.sk/pub/epel/%(ver)d/%(arch)s/' ], testing = [ 'repo --name=EPEL-testing --baseurl=%(mirror)s/mirrors/epel/testing/%(ver)d/Everything/%(arch)s/' ] ) el9 = el8 el10 = el8 extra_pkgs = ('pkgs' in cmd_line) or ('packages' in cmd_line) cmd_line_testing = 'testing' in cmd_line cmd_line_repo = cmd_line.get('inst.repo') or cmd_line.get('repo') cmd_line_pkgrepo = cmd_line.get('pkgrepo', '').split(',') cmd_line_packages = cmd_line.get('pkgs') or cmd_line.get('packages') def get(self, distro): grps = getattr(self, distro) ret = grps['base'] for pkgrepo in self.cmd_line_pkgrepo: if pkgrepo=='none': ret = [] elif grps.get(pkgrepo): ret.extend(grps[pkgrepo]) if self.cmd_line_testing: ret.extend(grps['testing']) if self.extra_pkgs: if 'epel' in grps: ret.extend(grps['epel']) ret.extend(grps['other']) return ret def repo_update(self, distro, version, arch): if type(version)==int: ver_short = str(version) else: ver_short = version.split('.')[0] if hasattr(self, distro+ver_short): repolist = self.get(distro+ver_short) else: repolist = self.get(distro) for repo in repolist: if repo.startswith('url') and self.cmd_line_repo: print('url --url=%s' % self.cmd_line_repo) else: print(repo % dict( mirror='http://ftp.upjs.sk/pub', variant=distribution_variant, dir='releases', ver=version, arch=arch )) def geturl(self, url): if '://' not in url: url = os.path.join(os.path.dirname(cmd_line['ks']), url) #return urlopen(url).read() return '%include ' + url def package_update(self, distro, version): distro_packages = dict( fc = ['gdisk'], el = ['epel-release'] ) group_packages = dict( desktop = 'desktop.pkgs' ) print('%packages --ignoremissing') print('\n'.join(distro_packages.get(distro))) if self.cmd_line_packages: for pkg in self.cmd_line_packages.split(','): if pkg in group_packages: print(self.geturl(group_packages[pkg])) else: print(pkg) print('%end') def lang(lang): if not lang: lang = 'sk_SK --addsupport=en_US' langs = dict( en = 'en_US', sk = 'sk_SK --addsupport=en_US', cs = 'cs_CZ --addsupport=en_US' ) if '_' not in lang and lang in langs: lang = langs[lang] if '.' not in lang: lang += '.UTF-8' print('lang', lang) def url_get(src, dst=None): url = os.path.join(cmd_line.get('config'), src) sys.stderr.write('Downloading script: %s\n' % url) if cmd_line.get('config').startswith('https://'): try: import requests req = requests.get(url) try: req.raise_for_status() except Exception as err: sys.stderr.write('Download error: %s [%s]\n' % (err[1], src)) return '' script = req.content except ImportError: import pycurl content = [] c = pycurl.Curl() c.setopt(pycurl.WRITEFUNCTION, content.append) c.setopt(pycurl.URL, url) try: c.perform() c.close() except pycurl.error as err: sys.stderr.write('Download error: %s [%s]\n' % (err[1], src)) return '' script = b''.join(content).decode('utf8') else: try: script = urlopen(url).read().decode('utf8') except HTTPError as err: sys.stderr.write('Download error: %s [%s]\n' % (err, src)) return '' if dst: open(dst, 'wt').write(script) os.chmod(dst, 0o755) return script if 'text' in cmd_line: print('text') if 'passwd' in cmd_line: if cmd_line['passwd'].startswith('$'): print('rootpw --iscrypted %s' % cmd_line['passwd']) else: print('rootpw %s' % cmd_line['passwd']) autopartition( cmd_line.get('disk') or cmd_line.get('disks'), cmd_line.get('part') ) repocfg = repocfg_class() repocfg.repo_update(distro[:2], int(distro[2:]), arch) repocfg.package_update(distro[:2], int(distro[2:])) lang(cmd_line.get('lang')) # custom configuration if 'config' in cmd_line: exec(url_get('pre.py')) if url_get('pre.sh', '/tmp/salstar-custom-pre.sh'): os.system('/tmp/salstar-custom-pre.sh') url_get('post.sh', '/tmp/salstar-custom-post.sh') PYTHON_EOF %end %post --nochroot --interpreter /bin/bash if [ -x /usr/libexec/platform-python ]; then # CentOS 8 PYTHON=/usr/libexec/platform-python elif [ -x /usr/bin/python3 ]; then # Fedora 25+ has only python3 PYTHON=/usr/bin/python3 else PYTHON=/usr/bin/python fi $PYTHON << PYTHON_EOF from __future__ import print_function import sys, os, re chroot = '/mnt/sysimage' repo_conf = '''\ [salstar.sk] name=SALstar.sk \$releasever - \$basearch - Base #baseurl=http://ftp.upjs.sk/pub/users/sal/Fedora/\$releasever/ mirrorlist=https://www.salstar.sk/download/mm/%(distro)s/salstar/\$releasever/\$basearch gpgkey=https://www.salstar.sk/pub/sagator/SAGATOR-GPG-KEY https://www.salstar.sk/pub/sagator/salstar.sk-GPG-KEY gpgcheck=1 enabled=1 metadata_expire=1 [salstar.sk-modular] name=SALstar.sk \$releasever - \$basearch - Modular mirrorlist=https://www.salstar.sk/download/mm/%(distro)s/salstar-modular/\$releasever/\$basearch gpgkey=https://www.salstar.sk/pub/sagator/SAGATOR-GPG-KEY https://www.salstar.sk/pub/sagator/salstar.sk-GPG-KEY gpgcheck=1 enabled=0 metadata_expire=1 [salstar.sk-testing] name=SALstar.sk \$releasever - \$basearch - Testing mirrorlist=https://www.salstar.sk/download/mm/%(distro)s/salstar-testing/\$releasever/\$basearch gpgkey=https://www.salstar.sk/pub/sagator/SAGATOR-GPG-KEY https://www.salstar.sk/pub/sagator/salstar.sk-GPG-KEY gpgcheck=1 enabled=0 metadata_expire=1 [salstar.sk-builder] name=builder.salstar.sk baseurl=http://builder.salstar.sk/local/$releasever/$basearch/ gpgkey=https://www.salstar.sk/pub/sagator/SAGATOR-GPG-KEY https://www.salstar.sk/pub/sagator/salstar.sk-GPG-KEY gpgcheck=0 enabled=0 metadata_expire=1 ''' repo_key_sagator = '''\ -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.6 (GNU/Linux) mQGiBEWSfqgRBADu/silyFvlgwCzsu8Ha/llSd4MidiYIeWO8vLszSJn6kVo+xbF hDlj/UXHQVwp/Q6pqrrNxucJSFaE/qWi6KuZ4XqN2xWyUAjSCJ0BiUJyoTD9NOg3 yXyzXVLRklHaeOUdNF7e376Vfwsgix1wsehBh66vtyX1znDfWPcqfAZkpwCgsw95 2f/9qBYhCM61Nd/jFe6HbW0EANmlw/CIS+QNCQQPntXbL2rGD80VLXK6W+yIMuPu bkSn1PJpeO4BBjOynVqAuW4bERm5gSPDxBqE5vD9Ef8pqsoUHRIVTyj1Z2I1JbXn OjbUZmFcLdqVNNHNRu3Bkb8YLD3qdtG6CHG6CfowYXZfdaTVgyYIcLiStGNNKaXI omJQA/9YFv2xvP82xykZvHqx0hnn/NXFrSn+9TJGdDKOo/6/6AA6BwXRBwjHG1x2 1CCIbnTOmoDeBw5+hpVtLnACannT6VnNvZ+Zm+qoMBZ/E+2m6MkkywyP7oUIsxLp 7FnZHd/e6FGpgzup55mZboAgth/l4+w1ztF1aCGHu6ATsR3VWLQpSmFuIE9ORFJF SiAoU0FHQVRPUikgPG9uZHJlampAc2Fsc3Rhci5zaz6IYAQTEQIAIAUCRZJ+qAIb AwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEAiqUAZrgi/nW/QAoJfsGDX8h6WH JqwZbPlWrR9FRP8ZAJ94pSLvMM6rfXlMbeOejfvFcdwc8w== =R77L -----END PGP PUBLIC KEY BLOCK----- ''' repo_key_salstar_sk = '''\ -----BEGIN PGP PUBLIC KEY BLOCK----- mDMEZ1vEcBYJKwYBBAHaRw8BAQdA7CogCdsfdQ4zOhCMCp43gHEA1jW6SBv7Y9/5 dOtBmrq0L1NBTHN0YXIuc2sgUlBNIHJlcG9zaXRvcnkgMjAyNCA8cnBtQHNhbHN0 YXIuc2s+iJkEExYKAEEWIQTf2OC3RW3bklDphS/2eXGutIxkawUCZ1vEcAIbAwUJ BaOagAULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRD2eXGutIxkazafAPwN ASLc0I5Dpun5zBcFVWXBL9L+j0Nb/NNqiC55bpAhaAEAqg3RI4YluAJZm3v8b7at Mvjx1Nqe9K7HZz8jJO0u+AS4OARnW8RwEgorBgEEAZdVAQUBAQdAsZ4rm2+d/8Qk Fo8Qs2uCcvC/ukVa3CpVLwueA4i7KRYDAQgHiH4EGBYKACYWIQTf2OC3RW3bklDp hS/2eXGutIxkawUCZ1vEcAIbDAUJBaOagAAKCRD2eXGutIxkaymnAP4nM4y0doTX 4mcQcxsnY8usH5YVcSGoNfCoCEd40ijh6AEArKRRn0ImdB+IRjsM7WlHzo8Kibcx 7qiNuW2RsleC2Ak= =eWoI -----END PGP PUBLIC KEY BLOCK----- ''' # add access from gateway, if behind NAT gwaccess='' gwip=os.popen( """/sbin/ip route get to 1.1.1.1 | awk '\$2=="via" { print \$3 }'""" ).read().strip() if re.search('^(10|172\.(1[6-9]|2[0-9]|3[01])|192\.168)\.', gwip): gwaccess=','+gwip ssh_authorized_keys='''\ from="work.salstar.sk,158.197.240.41,work2.salstar.sk,158.197.2.8,work6.salstar.sk,2001:4118:400:1::/64%s" \ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJskfyU63Nm3lx7ZjEf0unaPDhqiNiOnLdBRHsRG7fBv ondrejj@work.salstar.sk ''' % gwaccess # detect distribution rh_release = open(chroot+'/etc/redhat-release').read() if re.compile('^(CentOS|Red Hat|AlmaLinux|Rocky|Scientific)').search(rh_release): distro = 'epel' else: distro = 'fedora' os.environ['CHROOT'] = chroot # add yum repository open(chroot+'/etc/yum.repos.d/salstar.repo', 'wt').write( repo_conf % dict(distro=distro) ) open(chroot+'/etc/pki/rpm-gpg/RPM-GPG-KEY-SAGATOR', 'wt').write( repo_key_sagator ) open(chroot+'/etc/pki/rpm-gpg/RPM-GPG-KEY-salstar.sk-2024', 'wt').write( repo_key_salstar_sk ) # add ssh key if not os.path.isdir(chroot+'/root/.ssh'): os.mkdir(chroot+'/root/.ssh') open(chroot+'/root/.ssh/authorized_keys', 'wt').write(ssh_authorized_keys) # custom configuration if os.path.exists('/tmp/salstar-custom-post.py'): exec(open('/tmp/salstar-custom-post.py').read()) if os.path.exists('/tmp/salstar-custom-post.sh'): os.system('/tmp/salstar-custom-post.sh') os.system('sync') PYTHON_EOF %end %include /tmp/salstar.ks