├── .travis.yml ├── README.md ├── TODO.md ├── defaults └── main.yml ├── files ├── auto_grow_partition-alpine.start ├── auto_grow_partition-debian-ubuntu.sh ├── autogrowpart-debian-ubuntu.service ├── hostname-openbsd-dhcp ├── issue.net ├── motd-image ├── sources-debian-ubuntu.list ├── ssh-host-keygen-alpine.start ├── ssh-host-keygen-debian-ubuntu.service └── ssh-host-keygen-openbsd.sh ├── handlers └── main.yml ├── meta └── main.yml ├── notes.md ├── tasks ├── cleanup-alpine.yml ├── cleanup-debian-ubuntu.yml ├── cleanup-openbsd.yml ├── customize-alpine.yml ├── customize-debian-ubuntu.yml ├── customize-openbsd.yml ├── growpartition-alpine.yml ├── growpartition-debian-ubuntu.yml ├── growpartition-openbsd.yml ├── initial-setup-alpine.yml ├── initial-setup-debian-ubuntu.yml ├── initial-setup-openbsd.yml ├── main.yml ├── networking-alpine.yml ├── networking-debian.yml ├── networking-openbsd.yml ├── networking-ubuntu.yml ├── packages-alpine.yml ├── packages-debian-ubuntu.yml ├── packages-openbsd.yml ├── serialconsole-alpine.yml ├── serialconsole-debian-ubuntu.yml ├── serialconsole-openbsd.yml ├── ssh-host-keygen-alpine.yml ├── ssh-host-keygen-debian-ubuntu.yml ├── ssh-host-keygen-openbsd.yml ├── swapfile-ubuntu.yml ├── timezone-alpine.yml └── timezone-most-distros.yml ├── templates ├── doas-openbsd.conf.j2 ├── hostname-openbsd.j2 ├── hosts.j2 ├── motd-verse-debian-ubuntu.sh.j2 ├── motd-welcome-debian-ubuntu.sh.j2 ├── motd-welcome-openbsd.sh.j2 ├── mygate-openbsd.j2 ├── network-interfaces.j2 ├── show-ip-address-alpine.sh.j2 ├── show-ip-address-debian.sh.j2 ├── show-ip-address-ubuntu.sh.j2 ├── show-ip-adress-openbsd.py.j2 ├── static-routed-ip-ubuntu.yaml.j2 ├── sudo-default-user.j2 ├── template-cleanup-alpine.sh.j2 ├── template-cleanup-debian-ubuntu.sh.j2 └── template-cleanup-openbsd.sh.j2 ├── tests ├── inventory └── test.yml └── vars └── main.yml /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | services: docker 3 | 4 | env: 5 | - distro: ubuntu1804 6 | - distro: debian10 7 | - distro: debian9 8 | - distro: alpine 9 | 10 | script: 11 | # Configure test script so we can run extra tests after playbook is run 12 | - export container_id=$(date +%s) 13 | - export cleanup=false 14 | 15 | # Download test shim. 16 | - wget -O ${PWD}/tests/test.sh https://gist.githubusercontent.com/chriswayg/7cf5afda283252219c37c447ccf3ad88/raw/ansible-role-test.sh 17 | - chmod +x ${PWD}/tests/test.sh 18 | 19 | # Run tests. 20 | - ${PWD}/tests/test.sh 21 | 22 | notifications: 23 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ansible-initial-server 2 | ====================== 3 | [![Build Status](https://travis-ci.org/chriswayg/ansible-initial-server.svg?branch=master)](https://travis-ci.org/chriswayg/ansible-initial-server) 4 | 5 | Initial configuration of a freshly installed Debian/Ubuntu or OpenBSD server to be used as a template in KVM. 6 | 7 | - use the latest Github **tagged version** which has been tested more thoroughly than 'master' 8 | 9 | Requirements 10 | ------------ 11 | 12 | ### Install from ISO (or use packer-proxmox-templates) 13 | - this role is used by [packer-proxmox-templates](https://github.com/chriswayg/packer-proxmox-templates) 14 | 15 | Use with a default install of **Debian 10** net-install ISO 16 | - installed from debian-10.x.y-amd64-netinst.iso 17 | - include ssh server 18 | - an auto-install 'preseed.cfg' file can be used to speed-up the initial install 19 | 20 | Use with a default install of **Ubuntu 18.04** server ISO 21 | - installed from ubuntu-18.04.x-server-amd64.iso (not live-server!) 22 | - Software: Standard System Utilities and SSH Server (Standard and Server in Ubuntu) 23 | - Default user `deploy` (customizable) 24 | - Partitions: 1 primary ext4 root partition /dev/sda1 (no swap); Grub on MBR 25 | - an auto-install 'preseed.cfg' file can be used to speed-up the initial install 26 | 27 | Use with a default install of **Ubuntu 20.04** server ISO 28 | - installed from ubuntu-20.04.x-legacy-server-amd64.iso (not live-server!) 29 | - Software: Standard System Utilities and SSH Server (Standard and Server in Ubuntu) 30 | - Default user `deploy` (customizable) 31 | - Partitions: 1 primary ext4 root partition /dev/sda1 (no swap); Grub on MBR 32 | - an auto-install 'preseed.cfg' file can be used to speed-up the initial install 33 | 34 | Use with a default install of **OpenBSD 6.x** server ISO 35 | - installed from install6x.iso 36 | - installed with all file sets (or drop optional -comp* -game* -x*) 37 | - requires python to be installed first 38 | - an auto-install 'install.conf' file can be used to speed-up the initial install 39 | 40 | Use with a default install of **Alpine 3.x** server ISO 41 | - installed from alpine-virt-3.x.y-x86_64.iso 42 | - an auto-install 'answers' file can be used to speed-up the initial install 43 | 44 | ### Configuration features - Debian/Ubuntu 45 | - qemu-guest-agent for Packer SSH and in Proxmox for shutdown and backups 46 | - haveged random number generator to speed up boot 47 | - passwordless sudo for default user 'deploy' (name can be changed) 48 | - SSH public key installed for default user with only key login permitted 49 | - no login for root (optional) 50 | - display IP and SSH fingerprint before console login 51 | - serial console 52 | - generates new SSH host keys on first boot to avoid duplicates in cloned VMs 53 | - automatically grow partition after resizing VM disk 54 | - optional SSH warning banner 55 | - optional Verse of the Day displayed on motd 56 | 57 | ### Configuration features - Alpine 58 | - qemu-guest-agent for Packer SSH and in Proxmox for shutdown and backups 59 | - passwordless sudo for default user 'deploy' (name can be changed) 60 | - SSH public key installed for default user with only key login permitted 61 | - no login for root (optional) 62 | - display IP and SSH fingerprint before console login 63 | - serial console 64 | - generates new SSH host keys on first boot to avoid duplicates in cloned VMs 65 | - automatically grow partition after resizing VM disk 66 | - optional SSH warning banner 67 | 68 | ### Configuration features - OpenBSD 69 | - passwordless doas for default user 'deploy' (name can be changed) 70 | - SSH public key installed for default user with only key login permitted 71 | - no login for root (optional) 72 | - display IP and SSH fingerprint before console login 73 | - serial console 74 | - generates new SSH host keys on first boot to avoid duplicates in cloned VMs 75 | - optional SSH warning banner 76 | 77 | #### Vanilla 78 | Running the role with the `vanilla` tag will only make minimal modifications to the system: 79 | - passwordless sudo/doas for default user 'deploy' (name can be changed) 80 | - SSH public key installed for default user with only key login permitted 81 | - no login for root 82 | - serial console 83 | - no change to repositories 84 | - set hostname and /etc/hosts 85 | - set timezone 86 | - no customization to motd, bashrc, etc. 87 | - will not disable Ubuntu swapfile 88 | - will not display IP and SSH fingerprint before console login 89 | - will not automatically grow partition after resizing VM disk 90 | - check tasks/main.yml and other task files to see what else is excluded 91 | 92 | ### After Installation from ISO 93 | - check networking, that the VM is reachable via SSH 94 | 95 | Important Role Variables 96 | ------------------------ 97 | `iserver_user` - Username of the default user 98 | 99 | `iserver_[OS]_repos` - List of repositories which will replace the initial defaults 100 | 101 | `iserver_[OS]_packages` - List of system utilities to install 102 | 103 | `iserver_hostname` and `iserver_domain` - Hostname and domain name 104 | 105 | `iserver_sshkey` - SSH-key of the default user 106 | 107 | Important Flags 108 | --------------- 109 | `iserver_is_a_vm` - set to False when using the role on bare metal server 110 | 111 | `iserver_lock_root` - set to True in order to disallow root login even on console 112 | 113 | `iserver_static_ip` - set to True if configuring a static IP 114 | 115 | `iserver_verse_enabled` - show a Verse of the Day on motd 116 | 117 | Tags 118 | ---- 119 | `vanilla` - only make minimal modifications 120 | 121 | `is_template` - include additional tasks useful for Proxmox templates 122 | 123 | `travis` - these tasks will be skipped in Travis testing 124 | 125 | `proxmox` - these tasks are only intended for Proxmox and should be skipped on a VPS (--skip-tags "proxmox") 126 | 127 | - Check which tasks are going to be executed (example) 128 | 129 | ``` 130 | ansible-playbook -v server-template.yml --tags "vanilla,is_template" --skip-tags "openbsd,alpine" --list-tasks 131 | ``` 132 | 133 | Example Playbook 134 | ---------------- 135 | 136 | Name of playbook: `server-template.yml` 137 | - make sure the correct username is used, which is the default user name set up during iso installation 138 | 139 | ```yml 140 | - name: Initial configuration of a server. 141 | hosts: servers 142 | user: deploy 143 | vars_files: 144 | - server-template-vars.yml 145 | roles: 146 | - role: ansible-initial-server 147 | vars: 148 | iserver_user: deploy 149 | ``` 150 | 151 | The Debian installer by default creates a root user with a root password, who is not permitted to SSH in to the server. Sudo is not installed by default. Therefore the default user can SSH into Debian, but has to use `su` to do roots tasks. There is no need to change `sshd_config` to `PermitRootLogin yes`, because this Ansible role can authenticate with the initial user password and then 'become' root using 'su'. 152 | 153 | For the first run on Debian (before sudo is active & the SSH key has been transferred): 154 | 155 | - `ansible-playbook --ask-pass --ask-become-pass -e iserver_become=su -v server-template.yml` 156 | 157 | 158 | For the first run on Ubuntu (before the SSH key has been transferred): 159 | 160 | - `ansible-playbook --ask-pass --ask-become-pass -v server-template.yml` 161 | 162 | 163 | For the first run on Alpine (as root before the SSH key has been transferred): 164 | 165 | - `ansible-playbook --ask-pass -v server-template.yml` 166 | 167 | For subsequent runs or on installations which have sudo/doas and SSH-key already enabled: 168 | 169 | - `ansible-playbook -v server-template.yml` 170 | 171 | License 172 | ------- 173 | 174 | MIT 175 | 176 | Author Information 177 | ------------------ 178 | 179 | Christian Wagner 180 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ### TODO 2 | 3 | # On Debian 11,(apparently required to) Add a Symlink for dynamic MOTD file, if it is missing 4 | - https://blog.tian.it/dynamic-motd-on-debian/ 5 | `ln -s /var/run/motd /etc/motd` 6 | 7 | * show IP etc is not working in a VPS with Debian 11 8 | - does it need to be in Proxmox? 9 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for ansible-initial-server 3 | 4 | iserver_is_a_vm: True 5 | ### SUDOERS ### 6 | # use the name of the default user which was used during installation 7 | # this Ansible role is not intended to add or change a default user 8 | iserver_user: deploy 9 | iserver_become: sudo ## do not chanage this (use doas for OpenBSD) ## 10 | iserver_kickstart_become: "{{ iserver_become }}" 11 | iserver_lock_root: False 12 | 13 | ### BASE PACKAGES ### 14 | iserver_debian_repos: 15 | ## Debian 10 buster example 16 | - deb http://repo.myloc.de/debian buster main non-free contrib 17 | - deb-src http://repo.myloc.de/debian buster main non-free contrib 18 | - deb http://security.debian.org/debian-security buster/updates main contrib non-free 19 | - deb-src http://security.debian.org/debian-security buster/updates main contrib non-free 20 | - deb http://deb.debian.org/debian buster-updates main contrib non-free 21 | - deb-src http://deb.debian.org/debian buster-updates main contrib non-free 22 | 23 | ## Debian 11 bullseye 24 | # - deb http://deb.debian.org/debian/ bullseye main 25 | # - deb-src http://deb.debian.org/debian/ bullseye main 26 | # - deb http://security.debian.org/debian-security bullseye-security main 27 | # - deb-src http://security.debian.org/debian-security bullseye-security main 28 | # - deb http://deb.debian.org/debian/ bullseye-updates main 29 | # - deb-src http://deb.debian.org/debian/ bullseye-updates main 30 | 31 | iserver_ubuntu_repos: 32 | ## Ubuntu 20.04 example 33 | - deb http://repo.myloc.de/ubuntu/ focal main restricted universe multiverse 34 | - deb-src http://repo.myloc.de/ubuntu/ focal main restricted universe multiverse 35 | - deb http://repo.myloc.de/ubuntu/ focal-updates main restricted universe multiverse 36 | - deb-src http://repo.myloc.de/ubuntu/ focal-updates main restricted universe multiverse 37 | - deb http://repo.myloc.de/ubuntu/ focal-backports main restricted universe multiverse 38 | - deb-src http://repo.myloc.de/ubuntu/ focal-backports main restricted universe multiverse 39 | - deb http://security.ubuntu.com/ubuntu focal-security main restricted universe multiverse 40 | - deb-src http://security.ubuntu.com/ubuntu focal-security main restricted universe multiverse 41 | 42 | iserver_alpine_repos: 43 | ## Alpine 3.11 example 44 | - 'http://nl.alpinelinux.org/alpine/v3.11/main' 45 | - 'http://nl.alpinelinux.org/alpine/v3.11/community' 46 | - '@edge http://nl.alpinelinux.org/alpine/edge/main' 47 | - '@testing http://nl.alpinelinux.org/alpine/edge/testing' 48 | - '@edgecommunity http://dl-cdn.alpinelinux.org/alpine/edge/community' 49 | 50 | # packages to install on every Debian/Ubuntu server 51 | iserver_deb_packages: 52 | - openssh-server # needed for Travis testing 53 | # - qemu-guest-agent # needed by packer 54 | - sudo 55 | #- python-apt # used by ansible (up to Debian 10) 56 | - python3-apt # used by ansible (from Debian 11) 57 | - haveged # prevent vm boot delays 58 | - net-tools # includes ifconfig, netstat 59 | - nano # set as default editor in .profile 60 | - psmisc # includes pstree 61 | - wget 62 | - curl 63 | # - lsof 64 | # - lshw 65 | # - ncdu 66 | # - netstat-nat 67 | # - dnsutils 68 | # - pv 69 | # - screen 70 | 71 | # packages to install on every OpenBSD server 72 | # qemu-guest-agent is not funtional on OpenBSD (see notes.md) 73 | iserver_obsd_packages: 74 | - python # required by ansible 75 | - colorls # used by .profile 76 | - nano # set as default editor in .profile 77 | - pstree 78 | - wget 79 | - curl 80 | 81 | # packages to install on every Alpine server 82 | iserver_alpine_packages: 83 | - openssh # needed for Travis testing 84 | # - qemu-guest-agent@edgecommunity # needed by packer 85 | - sudo # required 86 | - shadow # required for adduser with Ansible 87 | - nano # set as default editor in .profile 88 | - pstree 89 | - wget 90 | - curl 91 | 92 | ### NETWORKING ### 93 | iserver_hostname: iserver-kvm 94 | iserver_domain: unassigned.domain 95 | iserver_fqdn: "{{ iserver_hostname }}.{{ iserver_domain }}" 96 | 97 | iserver_interface: ens18 # this could be different in a VPS 98 | iserver_obsd_interface: vio0 99 | iserver_static_ip: false 100 | # Add here main IP of the host 101 | iserver_host_main_ip: 127.1.1.1 102 | # This IP will go into 'hosts' 103 | # - use a localhost IP for a template with DHCP 104 | # - or use a fixed routed external IP for servers 105 | # - which will also go into and 'interfaces' or 'netplan' 106 | iserver_ip: 127.0.1.1 107 | # use slash notation! /24 for 255.255.255.0 or /32 for 255.255.255.255 108 | iserver_netmask: "/32" 109 | iserver_nameservers: 1.1.1.1,1.0.0.1 110 | 111 | ### SSH ### 112 | iserver_ssh_port: 22 113 | # this picks up the dafault public key from the user 114 | iserver_sshkey: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}" 115 | iserver_ssh_banner: none # or /etc/ssh/issue.net 116 | 117 | ### CUSTOMIZE ### 118 | iserver_timezone: UTC 119 | 120 | # Added to profile in iserver_users_dirs (Debian, Ubuntu) 121 | iserver_deb_user_profile: | 122 | EDITOR=/usr/local/bin/nano 123 | 124 | iserver_bashrc_root_debian: | 125 | export PS1="\[\033[1;31m\][\u@\h:\w]#\[\033[0m\] " 126 | export LS_OPTIONS='--color=auto' 127 | eval "`dircolors`" 128 | alias ls='ls $LS_OPTIONS' 129 | alias ll='ls $LS_OPTIONS -l' 130 | alias l='ls $LS_OPTIONS -lA' 131 | 132 | iserver_bashrc_user_debian: | 133 | export PS1="\[\033[1;34m\][\u@\h:\w]$\[\033[0m\] " 134 | export LS_OPTIONS='--color=auto' 135 | eval "`dircolors`" 136 | alias ls='ls $LS_OPTIONS' 137 | alias ll='ls $LS_OPTIONS -l' 138 | alias l='ls $LS_OPTIONS -lA' 139 | 140 | # Ubuntu dircolors are already activated 141 | iserver_bashrc_root_ubuntu: | 142 | export PS1="\[\033[1;31m\][\u@\h:\w]#\[\033[0m\] " 143 | 144 | iserver_bashrc_user_ubuntu: | 145 | export PS1="\[\033[1;34m\][\u@\h:\w]$\[\033[0m\] " 146 | 147 | # OpenBSD profiles 148 | iserver_obsd_root_profile: | 149 | PS1='\[\033[1;31m\][\u@\h:\w]#\[\033[0m\] ' 150 | export PS1 151 | LSCOLORS=gxfxcxdxbxegedabagacad 152 | export LSCOLORS 153 | alias ls='colorls -G' 154 | EDITOR=/usr/local/bin/nano 155 | export EDITOR 156 | HISTFILE="$HOME/.ksh_history" 157 | HISTSIZE=5000 158 | 159 | iserver_obsd_user_profile: | 160 | PS1='\[\033[1;34m\][\u@\h:\w]$\[\033[0m\] ' 161 | export PS1 162 | LSCOLORS=gxfxcxdxbxegedabagacad 163 | export LSCOLORS 164 | alias ls='colorls -G' 165 | EDITOR=/usr/local/bin/nano 166 | export EDITOR 167 | HISTFILE="$HOME/.ksh_history" 168 | HISTSIZE=5000 169 | 170 | # Alpine profiles 171 | iserver_alpine_root_profile: | 172 | export PS1="\[\033[1;31m\][\u@\h:\w]#\[\033[0m\] " 173 | export LS_OPTIONS='--color=auto' 174 | eval "`dircolors`" 175 | alias ls='ls $LS_OPTIONS' 176 | export EDITOR=/usr/bin/nano 177 | 178 | iserver_alpine_user_profile: | 179 | export PS1="\[\033[1;34m\][\u@\h:\w]$\[\033[0m\] " 180 | export LS_OPTIONS='--color=auto' 181 | eval "`dircolors`" 182 | alias ls='ls $LS_OPTIONS' 183 | export EDITOR=/usr/bin/nano 184 | 185 | # motd 186 | iserver_motd_welcome: | 187 | #cat /etc/motd-image 188 | echo "" 189 | echo " Welcome to $PRETTY_NAME Server" 190 | echo "" 191 | 192 | # name of disk to show available space on login 193 | iserver_main_disk: sda1 194 | 195 | # needs 'verse' installed 196 | iserver_verse_enabled: False 197 | -------------------------------------------------------------------------------- /files/auto_grow_partition-alpine.start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Ansible managed 3 | # Alpine 4 | 5 | date >>/var/log/growpart 2>&1 6 | growpart -N /dev/sda 2 >>/var/log/growpart 2>&1 7 | if [ $? -eq 0 ]; then 8 | echo "* auto-growing and resizing /dev/sda2" >>/var/log/growpart 2>&1 9 | growpart /dev/sda 2 >>/var/log/growpart 2>&1 10 | resize2fs /dev/sda2 >>/var/log/growpart 2>&1 11 | fi 12 | -------------------------------------------------------------------------------- /files/auto_grow_partition-debian-ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Ansible managed 3 | # Debian, Ubuntu 4 | 5 | growpart -N /dev/sda 1 6 | if [ $? -eq 0 ]; then 7 | echo "* auto-growing and resizing /dev/sda1" 8 | growpart /dev/sda 1 9 | resize2fs /dev/sda1 10 | fi 11 | -------------------------------------------------------------------------------- /files/autogrowpart-debian-ubuntu.service: -------------------------------------------------------------------------------- 1 | # Ansible managed 2 | # Debian, Ubuntu 3 | 4 | [Unit] 5 | Description=Automatically Grow Partition after resize by Proxmox. 6 | 7 | [Service] 8 | Type=simple 9 | ExecStart=/bin/bash /usr/local/bin/auto_grow_partition.sh 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /files/hostname-openbsd-dhcp: -------------------------------------------------------------------------------- 1 | dhcp 2 | -------------------------------------------------------------------------------- /files/issue.net: -------------------------------------------------------------------------------- 1 | ****************************************************************************** 2 | You are accessing a private computer system which is restricted to authorized 3 | individuals! 4 | 5 | Unauthorized use is prohibited and subject to criminal, civil, security, or 6 | administrative proceedings and/or penalties. 7 | 8 | No expectation of privacy: System use indicates consent to monitoring, 9 | recording, and auditing of activity by authorized personnel. 10 | ****************************************************************************** 11 | -------------------------------------------------------------------------------- /files/motd-image: -------------------------------------------------------------------------------- 1 | 2 | _______s___$______________________S 3 | ______________$________$_________$$ 4 | ______$____________$________S____$$___S 5 | __________s___s_________$___$$___$$___$$ 6 | _____$______________s_______$$___$$___$$ 7 | ______________s___________s_$$___$$___$$ 8 | ____s____$___________$______$$___$$___$$ 9 | ______________$____________§§§§_§§§§_§§§§ 10 | ___s__________________s_____§§___§§___§§ 11 | ________s_____s_____________§§___§§___§§ 12 | __$_______________$$$__$____§§§§_§§_§§§§ 13 | _______$_______$$$$$$$_______§§§§§§§§§§ 14 | _s_________$$$$$$____$$$$________§§ 15 | _______$$$$$$___________$$$______§§ 16 | ___$$$$$$_____¶¶¶_________$$$____§§ 17 | $$$$_________¶¶¶¶¶¶_¶¶¶¶¶___$$$_§§§§ 18 | $$$$$$_________¶¶¶¶¶¶¶¶¶¶_____$$$§§§§ 19 | _$$$$$$_____¶¶¶¶¶¶¶¶¶¶__________$$$§§§§§§ 20 | ___$$$$$$__¶¶¶¶¶¶_¶¶¶¶¶¶___________$$$ 21 | ____$$$$$$$__________¶¶¶¶¶___________$$$ 22 | ______$$$$$$__________¶¶¶¶¶¶___________$$$ 23 | _______$$$$$$$____000000¶¶¶¶¶¶__________$$$$ 24 | _________$$$$$$__000______¶¶¶¶_______$$$_$$$ 25 | __________$$$$$$__0_______________$$$__$$$$ 26 | ____000_____$$$$$0_____________$$$__$$$$$ 27 | __000________$$$0$$$_________$$$_$$$$$ 28 | _00______0000000$$$$$$__$$$$__$$$$$ 29 | 0000000000_______$$$$$$$$__$$$$$ 30 | ____000____________$$$$$$$$$ 31 | ____________________$$$$$$ 32 | -------------------------------------------------------------------------------- /files/sources-debian-ubuntu.list: -------------------------------------------------------------------------------- 1 | # Ansible managed 2 | # Debian, Ubuntu 3 | # repos are in /etc/apt/sources.list.d/* 4 | # 5 | -------------------------------------------------------------------------------- /files/ssh-host-keygen-alpine.start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Ansible managed 3 | # Alpine 4 | 5 | # OpenSSH Server Key Re-Generation on first boot in new VM 6 | if [ -f /etc/run-ssh-host-keygen-once ]; then 7 | # remove all host keys first, so that all will be refreshed 8 | rm -v -f /etc/ssh/*key* 9 | ssh-keygen -A 10 | # ensure that /etc/issue gets updated with the new key 11 | /etc/network/if-up.d/show-ip-address update 12 | rm -v -f /etc/run-ssh-host-keygen-once 13 | fi 14 | -------------------------------------------------------------------------------- /files/ssh-host-keygen-debian-ubuntu.service: -------------------------------------------------------------------------------- 1 | # Ansible managed 2 | # Debian, Ubuntu 3 | 4 | [Unit] 5 | Description=OpenSSH Server Key Generation 6 | Before=ssh.service 7 | ConditionPathExists=/etc/run-ssh-host-keygen-once 8 | 9 | [Service] 10 | # remove all host keys first, so that all will be refreshed 11 | # (only executed on first boot) 12 | ExecStart=/bin/bash -c 'rm -v -f /etc/ssh/*key*' 13 | ExecStart=/bin/bash -c 'ssh-keygen -A' 14 | ExecStart=/bin/bash -c 'rm -v -f /etc/run-ssh-host-keygen-once' 15 | Type=oneshot 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /files/ssh-host-keygen-openbsd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Ansible managed 3 | # OpenBSD 4 | 5 | # OpenSSH Server Key Re-Generation on first boot in new VM 6 | if [ -f /etc/run-ssh-host-keygen-once ]; then 7 | # remove all host keys first, so that all will be refreshed 8 | rm -f /etc/ssh/*key* 9 | ssh-keygen -A 10 | rm -f /etc/run-ssh-host-keygen-once 11 | fi 12 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for ansible-initial-server 3 | 4 | # Alpine 5 | # Ensure that local auto-start service is enabled 6 | # - used to start ssh-host-keygen.sh on first boot and the auto-growpart script 7 | - name: Activate Autostart 8 | service: 9 | name: local 10 | runlevel: default 11 | enabled: yes 12 | become: true 13 | become_method: "{{ iserver_become }}" 14 | tags: 15 | - vanilla 16 | 17 | # Alpine, OpenBSD 18 | - name: Restart SSHD 19 | service: 20 | name: sshd 21 | state: restarted 22 | become: true 23 | become_method: "{{ iserver_become }}" 24 | tags: 25 | - vanilla 26 | 27 | # Debian, Ubuntu 28 | - name: Restart SSH 29 | service: 30 | name: ssh 31 | state: restarted 32 | become: true 33 | become_method: "{{ iserver_become }}" 34 | tags: 35 | - vanilla 36 | 37 | # Debian, Ubuntu 38 | - name: Apply Network Configuration 39 | service: 40 | name: systemd-logind 41 | state: restarted 42 | become: true 43 | become_method: "{{ iserver_become }}" 44 | tags: 45 | - vanilla 46 | - travis 47 | 48 | # OpenBSD 49 | - name: Apply Network Settings 50 | command: sh /etc/netstart 51 | become: true 52 | become_method: "{{ iserver_become }}" 53 | tags: 54 | - vanilla 55 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Christian Wagner 3 | description: Initial configuration of a freshly installed Debian server to be used as a template in KVM. 4 | license: MIT 5 | # Tested with Ansible 2.7 & 2.9 6 | min_ansible_version: 2.7 7 | platforms: 8 | - name: Debian 9 | versions: 10 | - stretch 11 | - buster 12 | - name: OpenBSD 13 | - name: Alpine 14 | 15 | galaxy_tags: [] 16 | # List tags for your role here, one per line. A tag is a keyword that describes 17 | # and categorizes the role. Users find roles by searching for tags. Be sure to 18 | # remove the '[]' above, if you add tags to this list. 19 | # 20 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 21 | # Maximum 20 tags per role. 22 | 23 | dependencies: [] 24 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ## QEMU Guest Agent not functional in OpenBSD 4 | 5 | - qemu-ga can only be installed via `pkg_add qemu` which pulls in many dependencies. 6 | - to try qemu enable 'QEMU Guest Agent' 'type ISA' in Proxmox 7 | - run interactively: `qemu-ga -vm isa-serial -p /dev/cua01` 8 | - shows: 'debug: disabling command: guest-network-get-interfaces' etc. 9 | - if connected, it will show "execute":"guest-network-get-interfaces", 10 | when entering the Summary tab on Proxmox 11 | or "execute":"guest-shutdown", if trying to rebbot from Proxmox GUI 12 | but Proxmox will show 'No network information' on the Summary tab 13 | - shutdown or reboot from Proxmox do not work 14 | - see here: https://www.qemu.org/docs/master/qemu-ga-ref.html 15 | 16 | ### qemu-ga configuration 17 | 18 | `nano /etc/qemu/qemu-ga.conf` 19 | 20 | ``` 21 | [general] 22 | daemonize = 0 23 | pidfile = /var/run/qemu-ga.pid 24 | verbose = 1 25 | method = isa-serial 26 | path = /dev/cua01 27 | statedir = /var/run 28 | ``` 29 | 30 | ## How .bashrc gets called in Debian 10 31 | 32 | ### user deploy logged in via SSH 33 | 34 | ``` 35 | ETC-PROFILE 36 | DOT-PROFILE (b4 calling bashrc) 37 | DOT-BASHRC DEPLOY (called from DOT-PROFILE) 38 | DOT-PROFILE (after calling bashrc) 39 | ``` 40 | 41 | ### root user logged in via SUDO 42 | 43 | - Login shell 44 | 45 | ``` 46 | [deploy@proxmox:~]$ sudo su - 47 | [deploy@proxmox:~]$ sudo su --login 48 | [deploy@proxmox:~]$ sudo -i 49 | ETC-PROFILE 50 | DOT-PROFILE ROOT (b4 calling bashrc) 51 | DOT-BASHRC ROOT 52 | DOT-PROFILE ROOT (after calling bashrc) 53 | ``` 54 | 55 | - No login shell 56 | 57 | ``` 58 | [deploy@proxmox:~]$ sudo -s 59 | [deploy@proxmox:~]$ sudo su 60 | DOT-BASHRC ROOT 61 | ``` 62 | -------------------------------------------------------------------------------- /tasks/cleanup-alpine.yml: -------------------------------------------------------------------------------- 1 | ### CLEANUP ### 2 | # Alpine 3 | 4 | - name: Copy the cleanup script. 5 | template: 6 | src: template-cleanup-alpine.sh.j2 7 | dest: /usr/local/bin/template-cleanup.sh 8 | mode: a+x 9 | 10 | - name: Run the cleanup script for VM templates (only once). 11 | shell: /usr/local/bin/template-cleanup.sh 12 | args: 13 | creates: /etc/.template-cleanup.done 14 | -------------------------------------------------------------------------------- /tasks/cleanup-debian-ubuntu.yml: -------------------------------------------------------------------------------- 1 | ### CLEANUP ### 2 | # Debian, Ubuntu 3 | 4 | # about 400MB on an Ubuntu image - @jarno https://askubuntu.com/a/620515/831854 5 | - name: Register previous kernel versions to be removed. 6 | shell: dpkg-query -W -f'${Package}\n' 'linux-*' | sed -nr 's/.*-([0-9]+(\.[0-9]+){2}-[^-]+).*/\1 &/p' | sort -k 1,1V | awk '($1==c){exit} {print $2}' c=$(uname -r | cut -f1,2 -d-) 7 | register: old_kernel_packages 8 | changed_when: false 9 | 10 | - name: Remove packages of previous kernel versions. 11 | apt: 12 | name: "{{ old_kernel_packages.stdout }}" 13 | state: absent 14 | purge: yes 15 | autoremove: yes 16 | 17 | - name: Copy the cleanup script. 18 | template: 19 | src: template-cleanup-debian-ubuntu.sh.j2 20 | dest: /usr/local/bin/template-cleanup.sh 21 | mode: a+x 22 | 23 | - name: Run the cleanup script for VM templates (only once). 24 | shell: /usr/local/bin/template-cleanup.sh 25 | args: 26 | creates: /etc/.template-cleanup.done 27 | -------------------------------------------------------------------------------- /tasks/cleanup-openbsd.yml: -------------------------------------------------------------------------------- 1 | ### CLEANUP ### 2 | # OpenBSD 3 | 4 | - name: Copy the cleanup script. 5 | template: 6 | src: template-cleanup-openbsd.sh.j2 7 | dest: /usr/local/bin/template-cleanup.sh 8 | mode: a+x 9 | 10 | - name: Run the cleanup script for VM templates (only once). 11 | shell: /usr/local/bin/template-cleanup.sh 12 | args: 13 | creates: /etc/.template-cleanup.done 14 | -------------------------------------------------------------------------------- /tasks/customize-alpine.yml: -------------------------------------------------------------------------------- 1 | ### CUSTOMIZE ### 2 | # Alpine 3 | 4 | - name: Move the original /etc/motd to back it up (once). 5 | command: mv /etc/motd /etc/motd.original 6 | args: 7 | creates: /etc/motd.original 8 | 9 | # This does not update the motd with a new os-release after initial creation. 10 | - name: Create the file which will show a welcome motd after login. 11 | shell: source /etc/os-release && printf "\n $PRETTY_NAME ($VERSION_ID) Server\n\n" > /etc/motd 12 | args: 13 | creates: /etc/motd 14 | 15 | # TODO show a more dynamic motd 16 | # - name: Copy the file which will show a welcome motd after login. 17 | # template: 18 | # src: motd-welcome-alpine.j2 19 | # dest: /etc/motd 20 | # mode: a+x 21 | 22 | - name: Add a block of commands in the root user profile. 23 | blockinfile: 24 | path: "/root/.profile" 25 | block: | 26 | {{ iserver_alpine_root_profile }} 27 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 28 | create: yes 29 | 30 | - name: Add a block of commands in the default user profile. 31 | blockinfile: 32 | path: "/home/{{ iserver_user }}/.profile" 33 | block: | 34 | {{ iserver_alpine_user_profile }} 35 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 36 | create: yes 37 | -------------------------------------------------------------------------------- /tasks/customize-debian-ubuntu.yml: -------------------------------------------------------------------------------- 1 | ### CUSTOMIZE ### 2 | # Debian, Ubuntu 3 | 4 | - name: Move the original /etc/motd to back it up (once). 5 | command: mv /etc/motd /etc/motd.original 6 | args: 7 | creates: /etc/motd.original 8 | when: ansible_distribution == 'Debian' 9 | 10 | - name: Copy the file which will show a welcome motd after login. 11 | template: 12 | src: motd-welcome-debian-ubuntu.sh.j2 13 | dest: /etc/update-motd.d/15-motd-welcome 14 | mode: a+x 15 | 16 | - name: Copy a motd ascii image file. 17 | copy: 18 | src: motd-image 19 | dest: /etc/motd-image 20 | 21 | - name: Ensure that verse is installed. 22 | apt: 23 | name: verse 24 | state: present 25 | when: iserver_verse_enabled 26 | 27 | - name: Copy the file which will show a verse and disk space via motd. 28 | template: 29 | src: motd-verse-debian-ubuntu.sh.j2 30 | dest: /etc/update-motd.d/85-motd-verse 31 | mode: a+x 32 | when: iserver_verse_enabled 33 | 34 | - name: Remove Ubuntu help-text from motd. 35 | file: 36 | path: /etc/update-motd.d/10-help-text 37 | mode: a-x 38 | when: ansible_distribution == 'Ubuntu' 39 | 40 | # this file is apparently not present in some installations 41 | - name: Check if livepatch motd file exists. 42 | stat: 43 | path: /etc/update-motd.d/80-livepatch 44 | register: file_result 45 | 46 | - name: Remove Ubuntu livepatch notifications from motd. 47 | file: 48 | path: /etc/update-motd.d/80-livepatch 49 | mode: a-x 50 | when: ansible_distribution == 'Ubuntu' and file_result.stat.exists 51 | 52 | - name: upgrade software 53 | command: /opt/software/bin/upgrade 54 | args: 55 | removes: etc/software/software.conf.upgrade 56 | 57 | - name: Remove Ubuntu ads from motd 58 | lineinfile: 59 | path: /etc/default/motd-news 60 | state: present 61 | regexp: '^ENABLED=' 62 | line: 'ENABLED=0' 63 | when: ansible_distribution == 'Ubuntu' 64 | 65 | - name: Remove legal notice from Ubuntu user logins. 66 | copy: 67 | content: " " 68 | dest: /etc/legal 69 | when: ansible_distribution == 'Ubuntu' 70 | 71 | - name: Add a block of commands to the root user profile. 72 | blockinfile: 73 | path: "/root/.profile" 74 | block: | 75 | {{ iserver_deb_user_profile }} 76 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 77 | 78 | - name: Add a block of commands to the default user profile. 79 | blockinfile: 80 | path: "/home/{{ iserver_user }}/.profile" 81 | block: | 82 | {{ iserver_deb_user_profile }} 83 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 84 | 85 | - name: Add a block of bashrc config to the root user (Debian). 86 | blockinfile: 87 | path: "/root/.bashrc" 88 | block: | 89 | {{ iserver_bashrc_root_debian }} 90 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 91 | when: ansible_distribution == 'Debian' 92 | 93 | - name: Add a block of bashrc config to the default user (Debian). 94 | blockinfile: 95 | path: "/home/{{ iserver_user }}/.bashrc" 96 | block: | 97 | {{ iserver_bashrc_user_debian }} 98 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 99 | when: ansible_distribution == 'Debian' 100 | 101 | - name: Add a block of bashrc config to the root user (Ubuntu). 102 | blockinfile: 103 | path: "/root/.bashrc" 104 | block: | 105 | {{ iserver_bashrc_root_ubuntu }} 106 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 107 | when: ansible_distribution == 'Ubuntu' 108 | 109 | - name: Add a block of bashrc config to the default user (Ubuntu). 110 | blockinfile: 111 | path: "/home/{{ iserver_user }}/.bashrc" 112 | block: | 113 | {{ iserver_bashrc_user_ubuntu }} 114 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 115 | when: ansible_distribution == 'Ubuntu' 116 | -------------------------------------------------------------------------------- /tasks/customize-openbsd.yml: -------------------------------------------------------------------------------- 1 | ### CUSTOMIZE ### 2 | # OpenBSD 3 | 4 | - name: Move the original /etc/motd to back it up (once). 5 | command: mv /etc/motd /etc/motd.original 6 | args: 7 | creates: /etc/motd.original 8 | 9 | - name: Copy script which will refresh the motd that is displayed after login. 10 | template: 11 | src: motd-welcome-openbsd.sh.j2 12 | dest: /usr/local/bin/motd-welcome-openbsd.sh 13 | mode: a+x 14 | 15 | - name: Enable the motd script to autostart. 16 | blockinfile: 17 | path: "/etc/rc.local" 18 | create: yes 19 | block: | 20 | if [ -x /usr/local/bin/motd-welcome-openbsd.sh ]; then 21 | echo "updating motd." 22 | /usr/local/bin/motd-welcome-openbsd.sh 23 | fi 24 | marker: "# {mark} ANSIBLE MANAGED BLOCK - MOTD" 25 | 26 | - name: Add a block of commands in the root user profile. 27 | blockinfile: 28 | path: "/root/.profile" 29 | block: | 30 | {{ iserver_obsd_root_profile }} 31 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 32 | 33 | - name: Add a block of commands in the default user profile. 34 | blockinfile: 35 | path: "/home/{{ iserver_user }}/.profile" 36 | block: | 37 | {{ iserver_obsd_user_profile }} 38 | marker: "# {mark} ANSIBLE MANAGED BLOCK" 39 | -------------------------------------------------------------------------------- /tasks/growpartition-alpine.yml: -------------------------------------------------------------------------------- 1 | # GROW PARTITION 2 | # Alpine 3 | 4 | - name: Ensure that prerequisites are installed. 5 | apk: 6 | name: "{{ packages }}" 7 | state: present 8 | vars: 9 | packages: 10 | - nettle@edge # A low-level cryptographic library (prereq for cloud-utils) 11 | - gnutls@edge # A TLS protocol implementation (prereq for cloud-utils) 12 | - cloud-utils@testing # Useful set of utilities for interacting with a cloud (growpart) 13 | - e2fsprogs-extra # Ext2/3/4 filesystem extra utilities (resize2fs) 14 | 15 | - name: Copy script to automatically Grow Partition after resize by Proxmox. 16 | copy: 17 | src: auto_grow_partition-alpine.start 18 | dest: /etc/local.d/05_auto_grow_partition.start 19 | mode: a+x 20 | notify: Activate Autostart 21 | -------------------------------------------------------------------------------- /tasks/growpartition-debian-ubuntu.yml: -------------------------------------------------------------------------------- 1 | # GROW PARTITION 2 | # Debian, Ubuntu 3 | 4 | - name: Ensure that cloud-guest-utils including growpart is installed. 5 | apt: 6 | name: cloud-guest-utils 7 | state: present 8 | 9 | - name: Copy systemd unitfile to automatically Grow Partition after resize by Proxmox. 10 | copy: 11 | src: autogrowpart-debian-ubuntu.service 12 | dest: /etc/systemd/system/autogrowpart.service 13 | mode: 0644 14 | register: enable_autogrowpart 15 | 16 | - name: Copy script to automatically Grow Partition after resize by Proxmox. 17 | copy: 18 | src: auto_grow_partition-debian-ubuntu.sh 19 | dest: /usr/local/bin/auto_grow_partition.sh 20 | mode: a+x 21 | 22 | - name: Enable the autogrowpart service. 23 | service: 24 | name: autogrowpart 25 | enabled: yes 26 | when: enable_autogrowpart is changed 27 | -------------------------------------------------------------------------------- /tasks/growpartition-openbsd.yml: -------------------------------------------------------------------------------- 1 | # GROW PARTITION 2 | # OpenBSD 3 | 4 | ## TODO find a replacement for growpart in OpenBSD 5 | ## There seem to be no cloud-utils or growpart for OpenBSD 6 | 7 | # - name: Ensure that prerequisites are installed. 8 | # openbsd_pkg: 9 | # name: "{{ packages }}" 10 | # state: present 11 | # vars: 12 | # packages: 13 | # - cloud-utils # Useful set of utilities for interacting with a cloud (growpart) 14 | 15 | # - name: Copy script to automatically Grow Partition after resize by Proxmox. 16 | # copy: 17 | # src: auto_grow_partition-openbsd.sh 18 | # dest: /usr/local/bin/auto_grow_partition.sh 19 | # mode: a+x 20 | -------------------------------------------------------------------------------- /tasks/initial-setup-alpine.yml: -------------------------------------------------------------------------------- 1 | ### Initial Setup ### 2 | # Alpine 3 | 4 | # Assumes that: Alpine SSH login with password for root allowed 5 | - name: KICKSTART - Alpine. 6 | block: 7 | - name: Ensure that sudo is installed. 8 | apk: 9 | name: sudo 10 | state: present 11 | 12 | ### SUDOERS ### 13 | - name: Ensure group "{{ iserver_user }}" exists 14 | group: 15 | name: "{{ iserver_user }}" 16 | state: present 17 | 18 | - name: Ensure that the default user "{{ iserver_user }}" exists 19 | user: 20 | name: "{{ iserver_user }}" 21 | group: "{{ iserver_user }}" 22 | password: '*' 23 | 24 | - name: Add the default user with passwordless sudo to sodoers.d 25 | template: 26 | src: sudo-default-user.j2 27 | dest: /etc/sudoers.d/default-user 28 | mode: 0440 29 | 30 | ## End of block KICKSTART 31 | become: true 32 | become_method: "{{ iserver_kickstart_become }}" 33 | tags: 34 | - vanilla 35 | 36 | ## Now that the default user can do passwordless sudo, run the rest with sudo. 37 | - name: MAIN 38 | block: 39 | - name: Reset SSH connection to allow user changes to affect 'current login user'. 40 | meta: reset_connection 41 | tags: 42 | - vanilla 43 | 44 | ### BASE PACKAGES ### 45 | - name: Base Packages. 46 | import_tasks: packages-alpine.yml 47 | 48 | ### NETWORKING ### 49 | - name: Create a blank /etc/issue, if it does not exist. 50 | file: 51 | path: /etc/issue 52 | state: touch 53 | modification_time: preserve 54 | access_time: preserve 55 | 56 | - name: Copy original /etc/issue (once). 57 | command: cp /etc/issue /etc/issue.original 58 | args: 59 | creates: /etc/issue.original 60 | 61 | - name: Set hostname. 62 | hostname: 63 | name: "{{ iserver_hostname }}" 64 | register: apply_network_config 65 | tags: 66 | - vanilla 67 | 68 | - name: Networking. 69 | import_tasks: networking-alpine.yml 70 | 71 | - name: Copy original hosts file to back it up (once). 72 | command: cp /etc/hosts /etc/hosts.original 73 | args: 74 | creates: /etc/hosts.original 75 | tags: 76 | - vanilla 77 | 78 | - name: Create new hosts file. 79 | template: 80 | src: hosts.j2 81 | dest: /etc/hosts 82 | register: apply_network_config 83 | tags: 84 | - vanilla 85 | - travis 86 | 87 | - name: Apply network configuration changes. 88 | service: 89 | name: networking 90 | state: restarted 91 | when: apply_network_config is changed 92 | tags: 93 | - vanilla 94 | - travis 95 | 96 | ### SSH ### 97 | - name: Ensure that sshd is enabled and started. 98 | service: 99 | name: sshd 100 | enabled: yes 101 | state: started 102 | 103 | - name: Ensure that the default user has a SSH authorized key. 104 | authorized_key: 105 | user: "{{ iserver_user }}" 106 | state: present 107 | key: "{{ iserver_sshkey }}" 108 | tags: 109 | - vanilla 110 | 111 | #'UseDNS no' avoids login delays when the remote client's DNS cannot be resolved 112 | - name: Apply SSHD configuration. 113 | lineinfile: 114 | path: /etc/ssh/sshd_config 115 | state: present 116 | regexp: '^#?\s*{{ item.key }}\s' 117 | line: '{{ item.key }} {{ item.value }}' 118 | validate: '/usr/sbin/sshd -t -f %s' 119 | with_items: 120 | - key: "PermitRootLogin" 121 | value: "no" 122 | - key: "PasswordAuthentication" 123 | value: "no" 124 | - key: "ChallengeResponseAuthentication" 125 | value: "no" 126 | - key: "UsePAM" 127 | value: "yes" 128 | - key: "UseDNS" 129 | value: "no" 130 | - key: "Banner" 131 | value: "{{ iserver_ssh_banner }}" 132 | - key: "Port" 133 | value: "{{ iserver_ssh_port }}" 134 | notify: Restart SSHD 135 | tags: 136 | - vanilla 137 | 138 | - name: Add a warning banner, shown before SSH login. 139 | copy: 140 | src: issue.net 141 | dest: "{{ iserver_ssh_banner }}" 142 | when: iserver_ssh_banner != "none" 143 | 144 | - name: New SSH host keys on first boot. 145 | import_tasks: ssh-host-keygen-alpine.yml 146 | # only run when is_template tag is specifically requested 147 | tags: 148 | - never 149 | - is_template 150 | 151 | ### CUSTOMIZE ### 152 | - name: Timezone. 153 | import_tasks: timezone-alpine.yml 154 | tags: 155 | - vanilla 156 | 157 | - name: Customize. 158 | import_tasks: customize-alpine.yml 159 | 160 | ## CLOUD ### 161 | - name: Serial Console. 162 | import_tasks: serialconsole-alpine.yml 163 | tags: 164 | - vanilla 165 | - travis 166 | 167 | # GROW PARTITION 168 | - name: Grow Partion Automatically. 169 | import_tasks: growpartition-alpine.yml 170 | 171 | ### CLEANUP ### 172 | - name: Cleanup. 173 | import_tasks: cleanup-alpine.yml 174 | # only run when is_template tag is specifically requested 175 | tags: 176 | - never 177 | - is_template 178 | 179 | - name: Lock root account to prevent logins for root from console. 180 | user: 181 | name: root 182 | # or use '*' 183 | password: '!' 184 | when: iserver_lock_root 185 | tags: 186 | - vanilla 187 | 188 | ## End of block MAIN 189 | become: true 190 | -------------------------------------------------------------------------------- /tasks/initial-setup-debian-ubuntu.yml: -------------------------------------------------------------------------------- 1 | ### Initial Setup ### 2 | # Debian, Ubuntu 3 | 4 | # For this block we use 'su' on first-run, because root cannot ssh into the server. 5 | - name: KICKSTART - Debian, Ubuntu. 6 | block: 7 | - name: Ensure that sudo is installed. 8 | apt: 9 | name: "{{ packages }}" 10 | state: present 11 | vars: 12 | packages: 13 | - sudo 14 | 15 | ### SUDOERS ### 16 | - name: Ensure group "{{ iserver_user }}" exists 17 | group: 18 | name: "{{ iserver_user }}" 19 | state: present 20 | 21 | - name: Ensure that the default user "{{ iserver_user }}" exists 22 | user: 23 | name: "{{ iserver_user }}" 24 | group: "{{ iserver_user }}" 25 | password: '*' 26 | 27 | - name: Add the default user with passwordless sudo to sodoers.d 28 | template: 29 | src: sudo-default-user.j2 30 | dest: /etc/sudoers.d/default-user 31 | mode: 0440 32 | 33 | ## End of block KICKSTART 34 | become: true 35 | become_method: "{{ iserver_kickstart_become }}" 36 | tags: 37 | - vanilla 38 | 39 | ## TODO [WARNING]: reset_connection task does not support when conditional 40 | - name: Reset SSH connection to allow user changes to affect 'current login user'. 41 | meta: reset_connection 42 | tags: 43 | - vanilla 44 | 45 | ## Now that the default user can do passwordless sudo, run the rest with sudo. 46 | - name: MAIN 47 | block: 48 | ### BASE PACKAGES ### 49 | - name: Base Packages. 50 | import_tasks: packages-debian-ubuntu.yml 51 | 52 | ### NETWORKING ### 53 | - name: Create a blank /etc/issue, if it does not exist. 54 | file: 55 | path: /etc/issue 56 | state: touch 57 | modification_time: preserve 58 | access_time: preserve 59 | 60 | - name: Copy original /etc/issue (once). 61 | command: cp /etc/issue /etc/issue.original 62 | args: 63 | creates: /etc/issue.original 64 | 65 | - name: Set hostname. 66 | hostname: 67 | name: "{{ iserver_hostname }}" 68 | notify: 69 | - Apply Network Configuration 70 | tags: 71 | - vanilla 72 | - travis 73 | 74 | - name: Networking - Debian. 75 | import_tasks: networking-debian.yml 76 | when: ansible_distribution == 'Debian' 77 | tags: 78 | - travis 79 | 80 | - name: Networking - Ubuntu. 81 | import_tasks: networking-ubuntu.yml 82 | when: ansible_distribution == 'Ubuntu' 83 | tags: 84 | - travis 85 | 86 | - name: Copy original hosts file to back it up (once). 87 | command: cp /etc/hosts /etc/hosts.original 88 | args: 89 | creates: /etc/hosts.original 90 | tags: 91 | - vanilla 92 | 93 | - name: Create new hosts file. 94 | template: 95 | src: hosts.j2 96 | dest: /etc/hosts 97 | notify: 98 | - Apply Network Configuration 99 | tags: 100 | - vanilla 101 | - travis 102 | 103 | ### SSH ### 104 | - name: Ensure that the default user has a SSH authorized key. 105 | authorized_key: 106 | user: "{{ iserver_user }}" 107 | state: present 108 | key: "{{ iserver_sshkey }}" 109 | tags: 110 | - vanilla 111 | 112 | # Needed when running in Travis to validate sshd_config. 113 | - name: Ensure that SSH service is started. 114 | service: 115 | name: ssh 116 | state: started 117 | 118 | #'UseDNS no' avoids login delays when the remote client's DNS cannot be resolved 119 | - name: Apply SSHD configuration. 120 | lineinfile: 121 | path: /etc/ssh/sshd_config 122 | state: present 123 | regexp: '^#?\s*{{ item.key }}\s' 124 | line: '{{ item.key }} {{ item.value }}' 125 | validate: '/usr/sbin/sshd -t -f %s' 126 | with_items: 127 | - key: "PermitRootLogin" 128 | value: "no" 129 | - key: "PasswordAuthentication" 130 | value: "no" 131 | - key: "ChallengeResponseAuthentication" 132 | value: "no" 133 | - key: "UsePAM" 134 | value: "yes" 135 | - key: "UseDNS" 136 | value: "no" 137 | - key: "Banner" 138 | value: "{{ iserver_ssh_banner }}" 139 | - key: "Port" 140 | value: "{{ iserver_ssh_port }}" 141 | notify: Restart SSH 142 | tags: 143 | - vanilla 144 | 145 | - name: Add a warning banner, shown before SSH login. 146 | copy: 147 | src: issue.net 148 | dest: "{{ iserver_ssh_banner }}" 149 | when: iserver_ssh_banner != "none" 150 | 151 | - name: New SSH host keys on first boot - Debian, Ubuntu 152 | import_tasks: ssh-host-keygen-debian-ubuntu.yml 153 | # only run when is_template tag is specifically requested 154 | tags: 155 | - never 156 | - is_template 157 | 158 | ## CUSTOMIZE ### 159 | - name: Timezone. 160 | import_tasks: timezone-most-distros.yml 161 | tags: 162 | - vanilla 163 | - travis 164 | 165 | - name: Customize. 166 | import_tasks: customize-debian-ubuntu.yml 167 | 168 | ### CLOUD ### 169 | - name: Serial Console. 170 | import_tasks: serialconsole-debian-ubuntu.yml 171 | when: iserver_is_a_vm 172 | tags: 173 | - vanilla 174 | - travis 175 | - proxmox 176 | 177 | - name: Remove Swapfile - Ubuntu. 178 | import_tasks: swapfile-ubuntu.yml 179 | when: iserver_is_a_vm and ansible_distribution == 'Ubuntu' 180 | tags: 181 | - proxmox 182 | 183 | # GROW PARTITION 184 | - name: Grow Partion Automatically. 185 | import_tasks: growpartition-debian-ubuntu.yml 186 | when: iserver_is_a_vm 187 | tags: 188 | - proxmox 189 | 190 | ### CLEANUP ### 191 | - name: Cleanup. 192 | import_tasks: cleanup-debian-ubuntu.yml 193 | # only run when is_template tag is specifically requested 194 | when: iserver_is_a_vm 195 | tags: 196 | - never 197 | - is_template 198 | 199 | - name: Lock root account to prevent logins for root from console. 200 | user: 201 | name: root 202 | # or use '*' 203 | password: '!' 204 | when: iserver_lock_root 205 | tags: 206 | - vanilla 207 | 208 | ## End of block MAIN 209 | become: true 210 | -------------------------------------------------------------------------------- /tasks/initial-setup-openbsd.yml: -------------------------------------------------------------------------------- 1 | ### Initial Setup ### 2 | # OpenBSD 3 | 4 | - name: KICKSTART - OpenBSD. 5 | block: 6 | ### SUDOERS ### 7 | # TODO check that creating a new user works as expected 8 | # - name: Ensure group "{{ iserver_user }}" exists 9 | # group: 10 | # name: "{{ iserver_user }}" 11 | # state: present 12 | # 13 | # - name: Ensure that the default user "{{ iserver_user }}" exists 14 | # user: 15 | # name: "{{ iserver_user }}" 16 | # group: "{{ iserver_user }}" 17 | # password: '*' 18 | 19 | - name: Check if iserver_user exists. 20 | shell: id -u "{{ iserver_user }}" > /dev/null 2>&1 21 | ignore_errors: True 22 | changed_when: false 23 | register: user_exists 24 | 25 | - name: Add the default user with passwordless doas. 26 | template: 27 | src: doas-openbsd.conf.j2 28 | dest: /etc/doas.conf 29 | mode: 0440 30 | when: user_exists is succeeded 31 | ## End of block KICKSTART 32 | become: true 33 | become_method: "{{ iserver_kickstart_become }}" 34 | tags: 35 | - vanilla 36 | 37 | - name: Reset SSH connection to allow user changes to affect 'current login user'. 38 | meta: reset_connection 39 | tags: 40 | - vanilla 41 | 42 | ## Now that the default user can do passwordless doas, run the rest with doas. 43 | - name: MAIN 44 | block: 45 | ### BASE PACKAGES ### 46 | - name: Base Packages - OpenBSD 47 | import_tasks: packages-openbsd.yml 48 | 49 | - name: Create symbolic links for python 2.7 50 | file: 51 | src: "{{ item.src }}" 52 | dest: "{{ item.dest }}" 53 | state: link 54 | loop: 55 | - { src: '/usr/local/bin/python2.7', dest: '/usr/bin/python' } 56 | - { src: '/usr/local/bin/python2.7', dest: '/usr/bin/python2' } 57 | - { src: '/usr/local/bin/python2.7', dest: '/usr/local/bin/python' } 58 | - { src: '/usr/local/bin/python2.7-2to3', dest: '/usr/local/bin/2to3' } 59 | - { src: '/usr/local/bin/python2.7-config', dest: '/usr/local/bin/python-config' } 60 | - { src: '/usr/local/bin/pydoc2.7', dest: '/usr/local/bin/pydoc' } 61 | 62 | ### NETWORKING ### 63 | - name: New SSH host keys on first boot. 64 | import_tasks: ssh-host-keygen-openbsd.yml 65 | # only run when is_template tag is specifically requested 66 | tags: 67 | - never 68 | - is_template 69 | 70 | - name: Set hostname. 71 | hostname: 72 | name: "{{ iserver_hostname }}" 73 | notify: 74 | - Apply Network Settings 75 | tags: 76 | - vanilla 77 | 78 | - name: Ensure that DHCP or static IP is configured. 79 | import_tasks: networking-openbsd.yml 80 | 81 | - name: Copy original hosts file to back it up (once). 82 | command: cp /etc/hosts /etc/hosts.original 83 | args: 84 | creates: /etc/hosts.original 85 | tags: 86 | - vanilla 87 | 88 | - name: Create new hosts file. 89 | template: 90 | src: hosts.j2 91 | dest: /etc/hosts 92 | notify: 93 | - Apply Network Settings 94 | tags: 95 | - vanilla 96 | 97 | ### SSH ### 98 | - name: Ensure that the default user has a SSH authorized key. 99 | authorized_key: 100 | user: "{{ iserver_user }}" 101 | state: present 102 | key: "{{ iserver_sshkey }}" 103 | tags: 104 | - vanilla 105 | 106 | #'UseDNS no' avoids login delays when the remote client's DNS cannot be resolved 107 | - name: Apply SSHD configuration. 108 | lineinfile: 109 | path: /etc/ssh/sshd_config 110 | state: present 111 | regexp: '^#?\s*{{ item.key }}\s' 112 | line: '{{ item.key }} {{ item.value }}' 113 | validate: '/usr/sbin/sshd -t -f %s' 114 | with_items: 115 | - key: "PermitRootLogin" 116 | value: "no" 117 | - key: "PasswordAuthentication" 118 | value: "no" 119 | - key: "ChallengeResponseAuthentication" 120 | value: "no" 121 | - key: "UseDNS" 122 | value: "no" 123 | - key: "Banner" 124 | value: "{{ iserver_ssh_banner }}" 125 | - key: "Port" 126 | value: "{{ iserver_ssh_port }}" 127 | notify: Restart SSHD 128 | tags: 129 | - vanilla 130 | 131 | - name: Add a warning banner, shown before SSH login. 132 | copy: 133 | src: issue.net 134 | dest: "{{ iserver_ssh_banner }}" 135 | when: iserver_ssh_banner != "none" 136 | 137 | ### CUSTOMIZE ### 138 | - name: Timezone. 139 | import_tasks: timezone-most-distros.yml 140 | tags: 141 | - vanilla 142 | 143 | - name: Customize. 144 | import_tasks: customize-openbsd.yml 145 | 146 | ### CLOUD ### 147 | - name: Serial Console. 148 | import_tasks: serialconsole-openbsd.yml 149 | tags: 150 | - vanilla 151 | 152 | # TODO GROW PARTITION 153 | # - name: Grow Partition Automatically. 154 | # import_tasks: growpartition-openbsd.yml 155 | 156 | - name: Patch OpenBSD with latest fixes. 157 | command: syspatch 158 | changed_when: false 159 | tags: 160 | - vanilla 161 | 162 | ### CLEANUP ### 163 | - name: Cleanup. 164 | import_tasks: cleanup-openbsd.yml 165 | # only runs when the is_template tag is specifically requested 166 | tags: 167 | - never 168 | - is_template 169 | 170 | - name: Lock root account to prevent logins for root from console. 171 | user: 172 | name: root 173 | # or use '*' 174 | password: '!' 175 | when: iserver_lock_root 176 | tags: 177 | - vanilla 178 | 179 | # End of block MAIN 180 | become: true 181 | become_method: "{{ iserver_become }}" 182 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # MAIN tasks file for ansible-initial-server 3 | 4 | ## TODO: see also individual TODOs in files 5 | ## - check if it works with a manual default Ubuntu 18.04 ISO live-server install (probably using 'su' is not needed) 6 | ## - use Ansible handlers for restarting all services (instaed of restarting inline, partially done) 7 | 8 | - name: Initial Setup - Debian, Ubuntu. 9 | import_tasks: initial-setup-debian-ubuntu.yml 10 | when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' 11 | tags: 12 | - debuntu 13 | 14 | - name: Initial Setup - OpenBSD. 15 | import_tasks: initial-setup-openbsd.yml 16 | when: ansible_distribution == 'OpenBSD' 17 | tags: 18 | - openbsd 19 | 20 | - name: Initial Setup - Alpine. 21 | import_tasks: initial-setup-alpine.yml 22 | when: ansible_distribution == 'Alpine' 23 | tags: 24 | - alpine 25 | -------------------------------------------------------------------------------- /tasks/networking-alpine.yml: -------------------------------------------------------------------------------- 1 | ### NETWORKING ### 2 | # Alpine 3 | 4 | - name: Copy script that runs when the network comes up to display IP and SSH fingerprint before login. 5 | template: 6 | src: show-ip-address-alpine.sh.j2 7 | dest: /etc/network/if-up.d/show-ip-address 8 | mode: a+x 9 | 10 | # TODO test Set fixed IP via network/interfaces in Alpine 11 | - name: Set fixed IP via network/interfaces. 12 | template: 13 | src: network-interfaces.j2 14 | dest: /etc/network/interfaces 15 | when: iserver_static_ip | bool 16 | tags: 17 | - vanilla 18 | -------------------------------------------------------------------------------- /tasks/networking-debian.yml: -------------------------------------------------------------------------------- 1 | ### NETWORKING ### 2 | # Debian 3 | 4 | # Note: run-parts doesn't run scripts with a .sh extension 5 | - name: Copy script that runs when the network comes up to display IP and SSH fingerprint before login. 6 | template: 7 | src: show-ip-address-debian.sh.j2 8 | dest: /etc/network/if-up.d/show-ip-address 9 | mode: a+x 10 | when: iserver_is_a_vm 11 | 12 | - name: Set fixed IP via network/interfaces. 13 | template: 14 | src: network-interfaces.j2 15 | dest: /etc/network/interfaces 16 | when: iserver_static_ip 17 | tags: 18 | - vanilla 19 | -------------------------------------------------------------------------------- /tasks/networking-openbsd.yml: -------------------------------------------------------------------------------- 1 | ### NETWORKING ### 2 | # OpenBSD 3 | 4 | # see notes.md and comments in hostname-openbsd.j2 5 | 6 | - name: Copy original /etc/gettytab (once). 7 | command: cp /etc/gettytab /etc/gettytab.original 8 | args: 9 | creates: /etc/gettytab.original 10 | 11 | - name: Copy script which will display IP and SSH fingerprint before login. 12 | template: 13 | src: show-ip-adress-openbsd.py.j2 14 | dest: /usr/local/bin/show-ip-address.py 15 | mode: a+x 16 | 17 | - name: Enable the show-ip-address script to autostart. 18 | blockinfile: 19 | path: "/etc/rc.local" 20 | create: yes 21 | block: | 22 | if [ -x /usr/local/bin/show-ip-address.py ]; then 23 | echo "updating display of IP and SSH fingerprint." 24 | /usr/local/bin/show-ip-address.py 25 | fi 26 | marker: "# {mark} ANSIBLE MANAGED BLOCK - IP" 27 | 28 | - name: Ensure that DHCP is configured. 29 | copy: 30 | src: hostname-openbsd-dhcp 31 | dest: /etc/hostname.{{ iserver_obsd_interface }} 32 | notify: 33 | - Apply Network Settings 34 | when: not iserver_static_ip 35 | tags: 36 | - vanilla 37 | 38 | - name: Ensure that DNS nameservers are configured. 39 | lineinfile: 40 | path: /etc/resolv.conf 41 | state: present 42 | regexp: '^nameserver \s' 43 | line: 'nameserver {{ item }}' 44 | with_items: "{{ iserver_nameservers }}" 45 | notify: 46 | - Apply Network Settings 47 | when: iserver_static_ip 48 | tags: 49 | - vanilla 50 | 51 | - name: Ensure that static IP is configured. 52 | template: 53 | src: hostname-openbsd.j2 54 | dest: /etc/hostname.{{ iserver_obsd_interface }} 55 | notify: 56 | - Apply Network Settings 57 | when: iserver_static_ip 58 | tags: 59 | - vanilla 60 | 61 | # not used 62 | # - name: Ensure that static IP gateway is configured. 63 | # template: 64 | # src: mygate-openbsd.j2 65 | # dest: /etc/mygate 66 | # notify: 67 | # - Apply Network Settings 68 | # when: iserver_static_ip 69 | # tags: 70 | # - vanilla 71 | -------------------------------------------------------------------------------- /tasks/networking-ubuntu.yml: -------------------------------------------------------------------------------- 1 | ### NETWORKING ### 2 | # Ubuntu 3 | 4 | - name: Copy script that runs when the network comes up to display IP and SSH fingerprint before login. 5 | template: 6 | src: show-ip-address-ubuntu.sh.j2 7 | dest: /etc/networkd-dispatcher/routable.d/99-show-ip-address 8 | mode: a+x 9 | 10 | - name: Set fixed IP 11 | block: 12 | # netplan only uses files with .yaml extension (not .yml or .bak) 13 | - name: Backup any existing netplan configs (once). 14 | shell: for f in *.yaml; do mv -v -- "$f" "${f%.yaml}.bak"; done 2> /dev/null || true 15 | args: 16 | chdir: /etc/netplan/ 17 | creates: /etc/netplan/01-static-routed-ip.yaml 18 | 19 | - name: Set fixed IP via Netplan. 20 | template: 21 | src: static-routed-ip-ubuntu.yaml.j2 22 | dest: /etc/netplan/01-static-routed-ip.yaml 23 | register: netplan_generate_config 24 | 25 | - name: Generating and Applying Netplan Configuration. 26 | command: netplan generate && netplan apply 27 | when: netplan_generate_config is changed 28 | ## End of block Set fixed IP 29 | when: iserver_static_ip 30 | tags: 31 | - vanilla 32 | -------------------------------------------------------------------------------- /tasks/packages-alpine.yml: -------------------------------------------------------------------------------- 1 | ### BASE PACKAGES ### 2 | # Alpine 3 | 4 | - name: Move original /etc/apk/repositories to back it up (once). 5 | command: mv /etc/apk/repositories /etc/apk/repositories.original 6 | args: 7 | creates: /etc/apk/repositories.original 8 | 9 | - name: Add a set of specified repos into /etc/apk/repositories. 10 | lineinfile: 11 | path: /etc/apk/repositories 12 | state: present 13 | create: yes 14 | regexp: "^{{ item }}" 15 | line: "{{ item }}" 16 | loop: "{{ iserver_alpine_repos }}" 17 | 18 | - name: Ensure that all packages are updated. 19 | apk: 20 | update_cache: yes 21 | changed_when: False 22 | 23 | - name: Ensure that all packages are upgraded. 24 | apk: 25 | upgrade: yes 26 | changed_when: False 27 | 28 | - name: Ensure that initial packages are installed. 29 | apk: 30 | name: "{{ iserver_alpine_packages }}" 31 | state: present 32 | -------------------------------------------------------------------------------- /tasks/packages-debian-ubuntu.yml: -------------------------------------------------------------------------------- 1 | ### BASE PACKAGES ### 2 | # Debian, Ubuntu 3 | 4 | # replaces sources.list repos. 5 | - name: Move original /etc/apt/sources.list to back it up (once). 6 | command: mv /etc/apt/sources.list /etc/apt/sources.list.original 7 | args: 8 | creates: /etc/apt/sources.list.original 9 | 10 | # this expects python-apt to be installed. 11 | # at least two repos need to be defined. repos are added to sources.list.d/ 12 | - name: Add a set of specified repositories into sources list directory. 13 | apt_repository: 14 | repo: "{{ item }}" 15 | state: present 16 | loop: "{{ iserver_debian_repos }}" 17 | when: ansible_distribution == 'Debian' 18 | 19 | - name: Add a set of specified repositories into sources list directory. 20 | apt_repository: 21 | repo: "{{ item }}" 22 | state: present 23 | loop: "{{ iserver_ubuntu_repos }}" 24 | when: ansible_distribution == 'Ubuntu' 25 | 26 | # so that Ubuntu will not complain about missing sources.list 27 | - name: Ensure that there is a sources.list file. 28 | copy: 29 | src: sources-debian-ubuntu.list 30 | dest: /etc/apt/sources.list 31 | force: no 32 | 33 | - name: Ensure that all packages are updated and upgraded. 34 | apt: 35 | upgrade: "yes" 36 | update_cache: yes 37 | cache_valid_time: 86400 #One day 38 | 39 | - name: Ensure that initial packages are installed. 40 | apt: 41 | name: "{{ iserver_deb_packages }}" 42 | state: present 43 | -------------------------------------------------------------------------------- /tasks/packages-openbsd.yml: -------------------------------------------------------------------------------- 1 | ### BASE PACKAGES ### 2 | # OpenBSD 3 | 4 | - name: Update all packages on the system 5 | openbsd_pkg: 6 | name: '*' 7 | state: latest 8 | tags: 9 | - vanilla 10 | 11 | - name: Ensure that initial packages are installed. 12 | openbsd_pkg: 13 | name: "{{ iserver_obsd_packages }}" 14 | state: present 15 | -------------------------------------------------------------------------------- /tasks/serialconsole-alpine.yml: -------------------------------------------------------------------------------- 1 | # SERIAL CONSOLE 2 | # Alpine 3 | 4 | - name: Change update-extlinux.conf to get a login prompt at a serial console. 5 | replace: 6 | path: /etc/update-extlinux.conf 7 | regexp: '(^.*)quiet(.*)' 8 | replace: '\1console=ttyS0,9600\2' 9 | register: update_extlinux 10 | 11 | - name: Activate serial console via update-extlinux. 12 | command: update-extlinux 13 | when: update_extlinux is changed 14 | 15 | - name: Disable inittab ttys to prevent lots of tty messages to be logged to syslog. 16 | replace: 17 | path: /etc/inittab 18 | regexp: '(^tty[0-9].*)' 19 | replace: '#\1' 20 | 21 | - name: Apply inittab settings to get a login prompt at a serial console. 22 | replace: 23 | path: /etc/inittab 24 | regexp: '^#?\s*(ttyS0::respawn:/sbin/getty -L ttyS0.*)' 25 | replace: '\1' 26 | -------------------------------------------------------------------------------- /tasks/serialconsole-debian-ubuntu.yml: -------------------------------------------------------------------------------- 1 | # SERIAL CONSOLE 2 | # Debian, Ubuntu 3 | 4 | - name: Enable the serial console by modifying grub. 5 | lineinfile: 6 | path: /etc/default/grub 7 | state: present 8 | regexp: '^GRUB_CMDLINE_LINUX_DEFAULT=' 9 | line: 'GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS0 console=tty0"' 10 | register: update_grub 11 | 12 | - name: Update grub. 13 | command: update-grub 14 | when: update_grub is changed 15 | -------------------------------------------------------------------------------- /tasks/serialconsole-openbsd.yml: -------------------------------------------------------------------------------- 1 | # SERIAL CONSOLE 2 | # OpenBSD 3 | 4 | - name: Configure the boot process to use the serial port as a console. 5 | copy: 6 | dest: /etc/boot.conf 7 | content: | 8 | set tty com0 9 | 10 | - name: Change /etc/ttys to get a login prompt at a serial console. 11 | lineinfile: 12 | path: /etc/ttys 13 | state: present 14 | regexp: '^console' 15 | line: 'console "/usr/libexec/getty std.9600" vt220 on secure' 16 | -------------------------------------------------------------------------------- /tasks/ssh-host-keygen-alpine.yml: -------------------------------------------------------------------------------- 1 | # SSH-HOST-KEYGEN (for KVM template) 2 | # Alpine 3 | 4 | - name: Copy script for OpenSSH Server Key Generation. 5 | copy: 6 | src: ssh-host-keygen-alpine.start 7 | dest: /etc/local.d/02_ssh-host-keygen.start 8 | mode: a+x 9 | register: enable_ssh_host_keygen 10 | notify: 11 | - Activate Autostart 12 | 13 | - name: Create a file so that ssh-host-keygen will run once. 14 | file: 15 | path: /etc/run-ssh-host-keygen-once 16 | state: touch 17 | when: enable_ssh_host_keygen is changed 18 | -------------------------------------------------------------------------------- /tasks/ssh-host-keygen-debian-ubuntu.yml: -------------------------------------------------------------------------------- 1 | # SSH-HOST-KEYGEN (for KVM template) 2 | # Debian, Ubuntu 3 | 4 | - name: Copy systemd unitfile for OpenSSH Server Key Generation. 5 | copy: 6 | src: ssh-host-keygen-debian-ubuntu.service 7 | dest: /etc/systemd/system/ssh-host-keygen.service 8 | mode: 0644 9 | register: enable_ssh_host_keygen 10 | 11 | - name: Enable ssh-host-keygen service that generates new SSH host keys on first boot. 12 | service: 13 | name: ssh-host-keygen 14 | enabled: yes 15 | when: enable_ssh_host_keygen is changed 16 | 17 | - name: Create a file so that ssh-host-keygen.service will run once. 18 | file: 19 | path: /etc/run-ssh-host-keygen-once 20 | state: touch 21 | when: enable_ssh_host_keygen is changed 22 | -------------------------------------------------------------------------------- /tasks/ssh-host-keygen-openbsd.yml: -------------------------------------------------------------------------------- 1 | # SSH-HOST-KEYGEN (for KVM template) 2 | # OpenBSD 3 | 4 | # TODO test with OpenBSD 5 | - name: Copy script for OpenSSH Server Key Generation. 6 | copy: 7 | src: ssh-host-keygen-openbsd.sh 8 | dest: /usr/local/bin/ssh-host-keygen.sh 9 | mode: a+x 10 | register: enable_ssh_host_keygen 11 | 12 | - name: Enable the ssh-host-keygen to autostart. 13 | blockinfile: 14 | path: "/etc/rc.local" 15 | create: yes 16 | block: | 17 | if [ -x /usr/local/bin/ssh-host-keygen.sh ]; then 18 | /usr/local/bin/ssh-host-keygen.sh 19 | fi 20 | marker: "# {mark} ANSIBLE MANAGED BLOCK - KEY" 21 | 22 | - name: Create a file so that ssh-host-keygen will run once. 23 | file: 24 | path: /etc/run-ssh-host-keygen-once 25 | state: touch 26 | when: enable_ssh_host_keygen is changed 27 | -------------------------------------------------------------------------------- /tasks/swapfile-ubuntu.yml: -------------------------------------------------------------------------------- 1 | # DISABLE SWAPFILE 2 | # Ubuntu 3 | 4 | # Ubuntu 'server' uses /swapfile 5 | - name: Disable swap if /swapfile exists. 6 | command: swapoff -a 7 | args: 8 | removes: /swapfile 9 | 10 | # Ubuntu 'live-server' uses /swap.img 11 | - name: Disable swap if /swap.img exists. 12 | command: swapoff -a 13 | args: 14 | removes: /swap.img 15 | 16 | - name: Ensure swapfile is disabled in /etc/fstab. 17 | mount: 18 | name: none 19 | src: /swapfile 20 | fstype: swap 21 | opts: sw 22 | passno: "0" 23 | dump: "0" 24 | state: absent 25 | 26 | - name: Ensure swap.img is disabled in /etc/fstab. 27 | mount: 28 | name: none 29 | src: /swap.img 30 | fstype: swap 31 | opts: sw 32 | passno: "0" 33 | dump: "0" 34 | state: absent 35 | 36 | - name: Ensure swapfile doesn't exist. 37 | file: 38 | path: /swapfile 39 | state: absent 40 | 41 | - name: Ensure swap.img doesn't exist. 42 | file: 43 | path: /swap.img 44 | state: absent 45 | -------------------------------------------------------------------------------- /tasks/timezone-alpine.yml: -------------------------------------------------------------------------------- 1 | # TIMEZONE 2 | # Alpine 3 | 4 | - name: Insert time zone into /etc/timezone. 5 | copy: 6 | dest: /etc/timezone 7 | content: | 8 | "{{ iserver_timezone }}" 9 | register: current_timezone 10 | 11 | # only execute this block if timezone has been changed 12 | - block: 13 | - name: Ensure time zone data is installed. 14 | apk: 15 | name: tzdata 16 | state: present 17 | 18 | - name: Copy time zone data to local time. 19 | command: cp "/usr/share/zoneinfo/{{ iserver_timezone }}" /etc/localtime 20 | 21 | - name: Ensure time zone data is removed. 22 | apk: 23 | name: tzdata 24 | state: absent 25 | # block end 26 | when: current_timezone.changed 27 | -------------------------------------------------------------------------------- /tasks/timezone-most-distros.yml: -------------------------------------------------------------------------------- 1 | # TIMEZONE 2 | # Debian, Ubuntu, OpenBSD 3 | 4 | - name: Set timezone. 5 | timezone: 6 | name: "{{ iserver_timezone }}" 7 | -------------------------------------------------------------------------------- /templates/doas-openbsd.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | # OpenBSD 3 | 4 | permit nopass keepenv {{ iserver_user }} 5 | -------------------------------------------------------------------------------- /templates/hostname-openbsd.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | # OpenBSD 3 | 4 | # -link: specifies the Hardware (link-level) MAC address. 5 | # -iface: The destination is directly reachable via the interface requiring no intermediary system to act as a gateway. 6 | # - both together: the gateway given is the MAC address of the host on the common network, indicating the interface to be used for transmission. 7 | # /etc/mygate is not used by OppenBSD with this config. 8 | # This is similar to onlink in Ubuntu or pointopoint in Debian. 9 | # see also: Network settings for OpenBSD system hosted at OVH – Frozen Geek Technology Blog 10 | 11 | inet {{ iserver_ip }}{{ iserver_netmask }} 12 | !sleep 2 13 | !route add -inet {{ iserver_host_main_ip }}/32 -link -iface {{ iserver_obsd_interface }} 14 | !route add -inet default {{ iserver_host_main_ip }} 15 | -------------------------------------------------------------------------------- /templates/hosts.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | # Debian, Ubuntu, Alpine, OpenBSD 3 | 4 | {{ iserver_ip }} {{ iserver_fqdn }} {{ iserver_hostname }} 5 | 127.0.0.1 localhost.localdomain localhost 6 | 7 | # The following lines are desirable for IPv6 capable hosts 8 | ::1 localhost ip6-localhost ip6-loopback 9 | ff02::1 ip6-allnodes 10 | ff02::2 ip6-allrouters 11 | -------------------------------------------------------------------------------- /templates/motd-verse-debian-ubuntu.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Ansible managed 3 | # Ubuntu, Debian 4 | 5 | # show a verse of the day, if verse is installed 6 | if [ -f /usr/bin/verse ]; then 7 | echo "" 8 | /usr/bin/verse 9 | echo "" 10 | fi 11 | 12 | w -s 13 | 14 | echo -e "\nDisk usage Size Used Avail Use% Mount" 15 | df -h | grep {{ iserver_main_disk }} # disk space 16 | echo "" 17 | -------------------------------------------------------------------------------- /templates/motd-welcome-debian-ubuntu.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Ansible managed 3 | # Ubuntu, Debian 4 | 5 | source /etc/os-release 6 | 7 | {{ iserver_motd_welcome }} 8 | -------------------------------------------------------------------------------- /templates/motd-welcome-openbsd.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Ansible managed 3 | # OpenBSD 4 | 5 | # Note that the /etc/motd file is modified by /etc/rc whenever the system is booted. 6 | # To keep any custom message intact, ensure that you leave *two blank lines* at the top, 7 | # or your message will be overwritten. 8 | topline=$(sysctl -n kern.version | sed 1q) 9 | printf "$topline\n" > /etc/motd 10 | printf "\n Welcome to $(echo $topline | cut -d' ' -f1,2) Server\n\n" >> /etc/motd 11 | -------------------------------------------------------------------------------- /templates/mygate-openbsd.j2: -------------------------------------------------------------------------------- 1 | {{ iserver_host_main_ip }} 2 | -------------------------------------------------------------------------------- /templates/network-interfaces.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | # Debian, Alpine 3 | # Only IPv4 4 | 5 | auto {{ iserver_interface }} 6 | iface {{ iserver_interface }} inet static 7 | # Additional IP used by VM 8 | address {{ iserver_ip }}{{ iserver_netmask }} 9 | # The Proxmox Main IP as gateway 10 | pointopoint {{ iserver_host_main_ip }} 11 | gateway {{ iserver_host_main_ip }} 12 | -------------------------------------------------------------------------------- /templates/show-ip-address-alpine.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # {{ ansible_managed }} 3 | # Alpine 4 | 5 | if [ "$1" != update ]; then 6 | if [ "$METHOD" = loopback ]; then 7 | exit 0 8 | fi 9 | 10 | # Only run from ifup. 11 | if [ "$MODE" != start ]; then 12 | exit 0 13 | fi 14 | fi 15 | 16 | cp /etc/issue.original /etc/issue 17 | sed -i '1s;^; \n;' /etc/issue 18 | printf "ECDSA key fingerprint:\n$(ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub | awk '/256 / { print $2 })\n" >> /etc/issue 19 | printf "\nSSH user: {{ iserver_user }} IP: $(ifconfig eth0 | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p')\n\n" >> /etc/issue 20 | -------------------------------------------------------------------------------- /templates/show-ip-address-debian.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # {{ ansible_managed }} 3 | # Debian 4 | 5 | if [ "$METHOD" = loopback ]; then 6 | exit 0 7 | fi 8 | 9 | # Only run from ifup. 10 | if [ "$MODE" != start ]; then 11 | exit 0 12 | fi 13 | 14 | # Only run from main interface. 15 | if [ "$IFACE" = {{ iserver_interface }} ]; then 16 | cp /etc/issue.original /etc/issue 17 | printf "SSH key fingerprint: \n$(ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub | awk '/256 / { print $2 })\n" >> /etc/issue 18 | printf "\nSSH user: {{ iserver_user }} Server IP: $(ip -o route get to 1.1.1.1 | sed -n 's/.*src \([0-9.]\+\).*/\1/p')\n\n" >> /etc/issue 19 | fi 20 | -------------------------------------------------------------------------------- /templates/show-ip-address-ubuntu.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Ansible managed 3 | # Ubuntu 4 | 5 | cp /etc/issue.original /etc/issue 6 | sleep 3 7 | printf "SSH key fingerprint: \n$(ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub | awk '/256 / { print $2 })\n" >> /etc/issue 8 | printf "\nSSH user: {{ iserver_user }} Server IP: $(ip -o route get to 1.1.1.1 | sed -n 's/.*src \([0-9.]\+\).*/\1/p')\n\n" >> /etc/issue 9 | EOF 10 | -------------------------------------------------------------------------------- /templates/show-ip-adress-openbsd.py.j2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # {{ ansible_managed }} 3 | # OpenBSD 4 | 5 | ## Because OpenBSD does not use /etc/issue we have to modify the /etc/gettytab instaed 6 | 7 | ## Original gettytab line - did not change in 25 years! (with 8 spaces indentation) 8 | # :np:im=\r\n%s/%m (%h) (%t)\r\n\r\n:sp#1200: 9 | ## restore original 10 | # sed -i 's/:np:im=.*/:np:im=\\r\\n%s\/%m (%h) (%t)\\r\\n\\r\\n:sp#1200:/' /etc/gettytab 11 | 12 | ## resulting line should look similar to this in /etc/gettytab: 13 | # :np:im=\r\n%s/%m (%h) (%t)\r\n\r\nECDSA key fingerprint SHA256 eegsh70oiZmpr1lxzeMAdMEnoLV8C5rnWJ9zLEizWhs\r\n\r\nSSH user deploy IP 10.10.10.69\r\n\r\n:sp#1200: 14 | 15 | ## previous shell script using sed caused errors when the key fingerprint 16 | ## contained a slash for example (too much trouble with escaping characters) 17 | #fingerprint=$(ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub | awk '/256 / { print $2 }' | sed 's/SHA256:/SHA256 /') 18 | #ipaddress=$(ifconfig {{ iserver_obsd_interface }} | awk '/inet / { print $2 }') 19 | ## escaped forward-slash '/' for sed, multiple escaped backslashes '\': once for double-quotes, again for sed 20 | #sed -i "s/:np:im=.*/:np:im=\\\r\\\n%s\/%m (%h) (%t)\\\r\\\n\\\r\\\nECDSA key fingerprint $fingerprint\\\r\\\n\\\r\\\nSSH user {{ iserver_user }} IP $ipaddress\\\r\\\n\\\r\\\n:sp#1200:/" /etc/gettytab 21 | 22 | import subprocess 23 | 24 | # remove the colon ':' using sed as the colon does not work in gettytab 25 | cmd="ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub | awk '/256 / { print $2 }' | sed 's/SHA256:/SHA256 /'" 26 | ps = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT) 27 | fingerprint = ps.communicate()[0].rstrip("\n") 28 | 29 | cmd="ifconfig {{ iserver_obsd_interface }} | awk '/inet / { print $2 }'" 30 | ps = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT) 31 | ipaddress = ps.communicate()[0].rstrip("\n") 32 | 33 | showinfo = ":np:im=\\r\\n%s/%m (%h) (%t)\\r\\n\\r\\nECDSA key fingerprint " + fingerprint + "\\r\\n\\r\\nSSH user {{ iserver_user }} IP " + ipaddress + "\\r\\n\\r\\n:sp#1200:" 34 | 35 | f = open("/etc/gettytab.original",'r') 36 | filedata = f.read() 37 | f.close() 38 | 39 | # we match the exact line in /etc/gettytab (using escape characters) 40 | newdata = filedata.replace(":np:im=\\r\\n%s/%m (%h) (%t)\\r\\n\\r\\n:sp#1200:",showinfo) 41 | 42 | f = open("/etc/gettytab",'w') 43 | f.write(newdata) 44 | f.close() 45 | -------------------------------------------------------------------------------- /templates/static-routed-ip-ubuntu.yaml.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | # Ubuntu 3 | 4 | network: 5 | version: 2 6 | renderer: networkd 7 | ethernets: 8 | {{ iserver_interface }}: 9 | dhcp4: no 10 | dhcp6: no 11 | addresses: [{{ iserver_ip }}{{ iserver_netmask }}] 12 | routes: 13 | - to: 0.0.0.0/0 14 | via: {{ iserver_host_main_ip }} 15 | on-link: true 16 | nameservers: 17 | addresses: [{{ iserver_nameservers }}] 18 | -------------------------------------------------------------------------------- /templates/sudo-default-user.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | # Debian, Ubuntu, Alpine 3 | 4 | {{ iserver_user }} ALL=(ALL:ALL) NOPASSWD: ALL 5 | -------------------------------------------------------------------------------- /templates/template-cleanup-alpine.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # {{ ansible_managed }} 3 | # Alpine 4 | 5 | [[ -f /etc/.template-cleanup.done ]] && exit 0 6 | 7 | echo "==> cleaning apk cache" 8 | rm -rf /var/cache/apk/* 9 | 10 | echo "==> cleaning ash history" 11 | unset HISTFILE 12 | rm -v /root/.ash_history /home/{{ iserver_user }}/.ash_history 2>/dev/null 13 | 14 | echo "==> clearing logs" 15 | >/var/log/messages 16 | 17 | echo "==> removing tmp files" 18 | rm -rf /tmp/* /var/tmp/* 19 | 20 | echo "==> Zero out the free space to save space in the final image" 21 | dd if=/dev/zero of=/EMPTY bs=1M || echo "dd exit code $? is suppressed" 22 | rm -f /EMPTY 23 | 24 | # Make sure we wait until all the data is written to disk, otherwise 25 | # Packer might quit too early before the large files are deleted 26 | sync 27 | 28 | # we only want this done once 29 | touch /etc/.template-cleanup.done 30 | 31 | exit 0 32 | -------------------------------------------------------------------------------- /templates/template-cleanup-debian-ubuntu.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # {{ ansible_managed }} 3 | # Debian, Ubuntu 4 | 5 | [[ -f /etc/.template-cleanup.done ]] && exit 0 6 | 7 | export DEBIAN_FRONTEND=noninteractive 8 | 9 | echo "==> removing X11 packages (server not desktop...)" 10 | apt-get -y remove libx11.* 11 | 12 | echo "==> purging packages which are no longer needed and cleaning apt cache" 13 | apt-get -y autoremove --purge 14 | apt-get -y clean 15 | apt-get -y autoclean 16 | 17 | echo "==> cleaning up dhcp leases" 18 | rm -v /var/lib/dhcp/* 2>/dev/null 19 | 20 | echo "==> cleaning bash history" 21 | unset HISTFILE 22 | rm /root/.bash_history /home/{{ iserver_user }}/.bash_history 2>/dev/null 23 | 24 | echo "==> clearing last login information" 25 | >/var/log/lastlog 26 | >/var/log/wtmp 27 | >/var/log/btmp 28 | >/var/log/auth.log 29 | 30 | echo "==> removing tmp files" 31 | rm -rf /tmp/* /var/tmp/* 32 | 33 | echo "==> Zero out the free space to save space in the final image" 34 | dd if=/dev/zero of=/EMPTY bs=1M || echo "dd exit code $? is suppressed" 35 | rm -f /EMPTY 36 | 37 | # Make sure we wait until all the data is written to disk, otherwise 38 | # Packer might quit too early before the large files are deleted 39 | sync 40 | 41 | # we only want this done once 42 | touch /etc/.template-cleanup.done 43 | 44 | exit 0 45 | -------------------------------------------------------------------------------- /templates/template-cleanup-openbsd.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # {{ ansible_managed }} 3 | # OpenBSD 4 | 5 | [ -f /etc/.template-cleanup.done ] && exit 0 6 | 7 | # echo "==> cleaning ash history" 8 | # unset HISTFILE 9 | # rm -v /root/.ash_history /home/{{ iserver_user }}/.ash_history 2>/dev/null 10 | # 11 | # echo "==> clearing logs" 12 | # >/var/log/messages 13 | # 14 | # echo "==> removing tmp files" 15 | # rm -rf /tmp/* /var/tmp/* 16 | 17 | echo "==> Zero out the free space to save space in the final image" 18 | dd if=/dev/zero of=/EMPTY bs=1M || echo "dd exit code $? is suppressed" 19 | rm -f /EMPTY 20 | 21 | # Make sure we wait until all the data is written to disk, otherwise 22 | # Packer might quit too early before the large files are deleted 23 | sync 24 | 25 | # we only want this done once 26 | touch /etc/.template-cleanup.done 27 | 28 | exit 0 29 | -------------------------------------------------------------------------------- /tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tests run by travis 3 | - hosts: all 4 | 5 | # a lot of things fail inside the docker containers used for testing 6 | # therefore those tasks are not run inside the docker container 7 | # the tag 'travis' marks those taks, which will be skipped 8 | roles: 9 | - role: role_under_test 10 | vars: 11 | iserver_sshkey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDK1zNq5zsVbbN/gLdYqxlb5CROsR1dBNBgRFzzCJUL3ncU2dDHLHWi0L/FafwWt6MQ7vePu7catLDegY2fs1QB0KYvy21fD3+9ONBs7KcFlmuyqjLJ9VAoLWW5Tv3I9eZNgpd9k6CvYphKa1Owq43ye+quQRI4J+2nb7Zhl2WTQ1N2WBwZbmf0ErTHwa+mC7frTRBYh6ddyXp9KRULH89y/6cVpL6uQyFzIr6yWowUbJ8lX3fA9e7RAxkG76X54sMa65oq3Bog04ylJ4n/xZCXO449BZjAZHcJuDcFLXrwIo52t+Q6gIEnXInTiii26/ZWbnzzheggjkpQ77tCg03t christian@Chris-GigaMac.local" 12 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for ansible-initial-server --------------------------------------------------------------------------------