Merge pull request #1 from glennklockwood/local

operate in local (pull) mode
This commit is contained in:
Glenn K. Lockwood 2018-10-29 12:12:25 -07:00 committed by GitHub
commit ec8c365094
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 591 additions and 131 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
site.retry local.retry

View File

@ -1,52 +1,46 @@
# Raspberry Pi Ansible # Raspberry Pi Ansible
Glenn K. Lockwood, August 2017 Glenn K. Lockwood, October 2018
## Introduction ## Introduction
This is an Ansible configuration that configures a fresh Raspbian installation This is an Ansible configuration that configures a fresh Raspbian installation
on Raspberry Pi. This is very much a work in progress and not intended to be on Raspberry Pi. It is intended to be run in local (pull) mode, where ansible
used by anyone but me. is running on the same Raspberry Pi to be configured.
## Bootstrapping on Raspbian ## Bootstrapping on Raspbian
If you want to use these playbooks to make a Raspberry Pi self-configure, You will need ansible installed on the Raspberry Pi being configured.
install Ansible by doing the following:
$ pip install --user ansible $ sudo apt-get install ansible
$ ssh-keygen
$ ssh-copy-id localhost
If not bootstrapping from the Raspberry Pi itself, you can instead do ## Configuration
$ ssh-copy-id pi@raspberrypi The `macaddrs` structure in _roles/common/vars/main.yml_ maps the MAC address of
a Raspberry Pi to its intended configuration state. Add your Raspberry Pi's MAC
address to that structure and set its configuration accordingly.
and authenticate using the default `raspberry` password. This will enable ## Running the playbook
key-based authentication to the remote Raspberry Pi to be configured.
You can ensure that Ansible is able to configure using the following:
$ ansible -i hosts all -m ping
You can also ensure that authentication also works.
$ ansible -i hosts -u pi --sudo-user root all -a "/usr/bin/id -u"
## Running the Playbook
This playbook will deactivate password authentication for the `pi` user since
it assumes that you have key-based authentication configured _before_ the
playbook is executed. Be sure that is the case or you may be locked out of
your Raspberry Pi altogether.
Then run the playbook: Then run the playbook:
$ ansible-playbook --inventory-file hosts --limit cloverfield --user pi --sudo site.yml $ sudo ansible-playbook local.yml
or The playbook will self-discover its settings, then idempotently configure the
Raspberry Pi.
$ ansible-playbook -i hosts -l clovermine -u pi -s site.yml ## After running the playbook
Raspbian should allow the `pi` user to sudo without a password. If not, run This playbook purposely requires a few manual steps _after_ running the playbook
using `--ask-become-pass` (or `-K`) and enter the sudo password (default would to ensure that it does not lock you out of your Raspberry Pi.
be `raspberry`) for the remote user (`pi`).
1. While logged in as pi, `sudo passwd glock` (or whatever username you created)
to set a password for that user. This is _not_ required to log in as that
user, but it _is_ required to `sudo` as that user. You may also choose to
set a password for the pi and/or root users.
2. `usermod --lock pi` to ensure that the default user is completely disabled.
## Acknowledgment
I stole a lot of knowledge from https://github.com/giuaig/ansible-raspi-config/.

4
hosts
View File

@ -1,3 +1 @@
cloverfield ansible_host=192.168.1.153 localhost ansible_connection=local
clovermine ansible_host=192.168.1.154
clovermill ansible_host=192.168.1.149

7
local.yml Normal file
View File

@ -0,0 +1,7 @@
---
- name: Raspberry Pi self configuration
hosts: localhost
user: root
connection: local
roles:
- common

View File

@ -1,3 +0,0 @@
---
- name: changed timezone
command: dpkg-reconfigure --frontend noninteractive tzdata

View File

@ -0,0 +1,25 @@
---
- name: get timezone via timedatectl
shell: "timedatectl | grep 'Time zone' | cut -d':' -f2 | cut -d'(' -f1 | sed -Ee 's/(^ *| *$)//g'"
register: linux_tz
changed_when: False
check_mode: no
- name: get locale
shell: "locale | grep ^LANG | cut -d= -f2"
register: linux_locale
changed_when: False
check_mode: no
- name: get x keyboard layout
shell: "localectl | awk '/X11 Layout/ {print $3}'"
register: linux_xkblayout
changed_when: False
check_mode: no
- name: set linux-config facts
set_fact:
linux_tz: "{{ linux_tz.stdout }}"
linux_locale: "{{ linux_locale.stdout }}"
linux_xkblayout: "{{ linux_xkblayout.stdout }}"
check_mode: no

View File

@ -1,26 +1,40 @@
--- ---
### Switch to non-default user as soon as possible if possible
#- name: does primary login user exist?
# local_action: "command ssh -q -o ConnectTimeout=3 -l {{ create_users[0].name }} {{ inventory_hostname }} /bin/true"
# register: user_exists
# ignore_errors: true
# changed_when: false
#
#- name: switch remote_user if possible
# remote_user: "{{ user_exists | success | ternary(omit, create_users[0].name) }}"
# command: "/bin/true"
# changed_when: false
### Set hostname # Gather facts specific to the Raspberry Pi platform
- include: raspi-facts.yml
- include: linux-facts.yml
# Basic hostname setup
- name: Get MAC address
debug:
msg: "{{ hostvars[inventory_hostname].ansible_default_ipv4.macaddress }}"
tags:
- raspi
- sw
- name: store MAC address
set_fact:
my_macaddr: "{{ hostvars[inventory_hostname].ansible_default_ipv4.macaddress }}"
tags:
- raspi
- sw
- name: store system configuration
set_fact:
myconfig: "{{ macaddrs[my_macaddr] }}"
tags:
- raspi
- sw
- name: set hostname - name: set hostname
hostname: name={{ inventory_hostname }} shell: "raspi-config nonint do_hostname {{ myconfig.hostname }}"
when: inventory_hostname is defined and ansible_nodename is defined when: raspi_hostname != myconfig.hostname
- name: update /etc/hosts with new hostname - name: update /etc/hosts with new hostname
lineinfile: lineinfile:
dest=/etc/hosts dest=/etc/hosts
regexp="^{{ ansible_default_ipv4.address }}" regexp="^{{ ansible_default_ipv4.address }}"
line="{{ ansible_default_ipv4.address }}{{'\t'}}{{ inventory_hostname }}.local{{'\t'}}{{ inventory_hostname }}" line="{{ ansible_default_ipv4.address }}{{'\t'}}{{ myconfig.hostname }}.{{ myconfig.domain }}{{'\t'}}{{ myconfig.hostname }}"
state=present state=present
- name: get rid of default 127.0.1.1 binding - name: get rid of default 127.0.1.1 binding
@ -29,78 +43,65 @@
regexp="^127.0.1.1" regexp="^127.0.1.1"
state=absent state=absent
### Configure /etc/hosts # Set timezone
- name: ensure that all local hosts are in /etc/hosts - name: set timezone
command: "timedatectl set-timezone {{ myconfig.timezone }}"
when: linux_tz != myconfig.timezone
# Set locale
- name: set locale
command: "raspi-config nonint do_change_locale {{ myconfig.locale }}"
when: "'locale' in myconfig and linux_locale != myconfig.locale"
# Set X keyboard layout
- name: set X11 keyboard layout
command: "raspi-config nonint do_configure_keyboard {{ myconfig.xkblayout }}"
when: "'xkblayout' in myconfig and myconfig.xkblayout != linux_xkblayout"
# Set wifi country
- name: set wifi country
command: "raspiconfig nonint do_wifi_country {{ myconfig.wifi_country }}"
when: "'wifi_country' in myconfig and myconfig.wifi_country != raspi_wifi_country"
# Enable sshd
- name: disable ssh login for user pi
lineinfile: lineinfile:
dest=/etc/hosts dest=/etc/ssh/sshd_config
line="{{ item.ip }}{{'\t'}}{{ item.name }}.local{{'\t'}}{{ item.name }}" line="DenyUsers pi"
state=present state=present
with_items: "{{etc_hosts_contents}}" tags:
- raspi
### Set timezone - name: enable SSH via raspi-config
- name: set /etc/timezone to America/Los_Angeles shell: "raspi-config nonint do_ssh 0"
copy: src=etc/timezone when: not raspi_ssh_enabled
dest=/etc/timezone tags:
owner=root - raspi
group=root
mode=0644
backup=yes
notify:
- changed timezone
### Uninstall Raspbian bloat # Other tasks
- name: remove raspbian bloat - include: software.yml
apt: - include: users.yml
name="{{ item }}" - include: raspi-config.yml
state=absent
with_items:
- wolfram-engine
- libreoffice*
- scratch
- minecraft-pi
- python-minecraftpi
- python3-minecraftpi
- sonic-pi
- dillo
- gpiciew
- penguinspuzzle
### Install required software # Configure firewall
- name: install basic software environment - name: allow SSH through UFW
apt: ufw:
name="{{ item }}" rule: allow
state=present port: ssh
update_cache=yes proto: tcp
with_items: log: yes
- vim
- git
- python-pip
### Create user accounts - name: set default incoming UFW policy to deny
- name: create users ufw:
user: name="{{ item.name }}" direction: incoming
comment="{{ item.comment }}" policy: deny
group="{{ item.group }}"
groups="{{ item.groups }}"
uid="{{ item.uid }}"
state=present
shell=/bin/bash
with_items: "{{ create_users }}"
tags: [ 'users' ]
- name: install ssh pubkeys for new users - name: set default outgoing UFW policy to deny
authorized_key: user="{{ item.name }}" ufw:
key="{{ item.pubkey }}" direction: outgoing
state=present policy: allow
with_items: "{{ create_users }}"
tags: [ 'users' ]
### disable the 'pi' user's ability to login in with password - name: enable UFW
### if you enable this, you may lock yourself out--you must make sure another ufw:
### user has been added with both sudo privileges and a password by which state: enabled
### sudo can be authenticated logging: yes
#- name: disable 'pi' user
# user: name="pi"
# password="*"
# state=present
# tags: [ 'users' ]

View File

@ -0,0 +1,98 @@
---
# Handle boot and autologin settings
- name: enable cli only
command: "raspi-config nonint do_boot_behaviour B1"
when: not myconfig.enable_gui and not myconfig.enable_autologin and (raspi_gui_enabled or raspi_autologin_enabled)
tags:
- raspi
- name: enable cli with autologin
command: "raspi-config nonint do_boot_behaviour B2"
when: not myconfig.enable_gui and myconfig.enable_autologin and (raspi_gui_enabled or not raspi_autologin_enabled)
tags:
- raspi
- name: enable desktop gui
command: "raspi-config nonint do_boot_behaviour B3"
when: myconfig.enable_gui and not myconfig.enable_autologin and (not raspi_gui_enabled or raspi_autologin_enabled)
tags:
- raspi
- name: enable desktop gui with autologin
command: "raspi-config nonint do_boot_behaviour B4"
when: myconfig.enable_gui and myconfig.enable_autologin and (not raspi_gui_enabled or raspi_autologin_enabled)
tags:
- raspi
- name: set bootwait option
command: "raspi-config nonint do_boot_wait {{ 0 if myconfig.enable_bootwait else 1 }}"
when: "'enable_bootwait' in myconfig and myconfig.enable_bootwait != raspi_bootwait_enabled"
tags:
- raspi
- name: set boot splash option
command: "raspi-config nonint do_boot_splash {{ 0 if myconfig.enable_bootsplash else 1 }}"
when: "'enable_bootsplash' in myconfig and myconfig.enable_bootsplash != raspi_bootsplash_enabled"
tags:
- raspi
- name: enable/disable camera
command: "raspi-config nonint do_camera {{ 0 if myconfig.enable_camera else 1 }}"
when: "'enable_camera' in myconfig and myconfig.enable_camera != raspi_camera_enabled"
tags:
- raspi
- name: enable/disable VNC server
command: "raspi-config nonint do_vnc {{ 0 if myconfig.enable_vnc else 1 }}"
when: "'enable_vnc' in myconfig and myconfig.enable_vnc != raspi_vnc_enabled"
tags:
- raspi
- name: enable/disable SPI
command: "raspi-config nonint do_spi {{ 0 if myconfig.enable_spi else 1 }}"
when: "'enable_spi' in myconfig and myconfig.enable_spi != raspi_spi_enabled"
tags:
- raspi
- name: enable/disable I2C
command: "raspi-config nonint do_i2c {{ 0 if myconfig.enable_i2c else 1 }}"
when: "'enable_i2c' in myconfig and myconfig.enable_i2c != raspi_i2c_enabled"
tags:
- raspi
- name: enable/disable serial
command: "raspi-config nonint do_serial {{ 0 if myconfig.enable_serial else 1 }}"
when: "'enable_serial' in myconfig and myconfig.enable_serial != raspi_serial_enabled"
tags:
- raspi
- name: enable/disable hardware serial
command: "raspi-config nonint do_serial_hw {{ 0 if myconfig.enable_serial_hw else 1 }}"
when: "'enable_serial_hw' in myconfig and myconfig.enable_serial_hw != raspi_serial_hw_enabled"
tags:
- raspi
- name: enable/disable onewire
command: "raspi-config nonint do_onewire {{ 0 if myconfig.enable_onewire else 1 }}"
when: "'enable_onewire' in myconfig and myconfig.enable_onewire != raspi_onewire_enabled"
tags:
- raspi
- name: enable/disable remote GPIO
command: "raspi-config nonint do_rgpio {{ 0 if myconfig.enable_rgpio else 1 }}"
when: "'enable_rgpio' in myconfig and myconfig.enable_rgpio != raspi_rgpio_enabled"
tags:
- raspi
- name: enable/disable HDMI overscan
command: "raspi-config nonint do_overscan {{ 0 if myconfig.enable_overscan else 1 }}"
when: "'enable_overscan' in myconfig and myconfig.enable_overscan != raspi_overscan_enabled"
tags:
- raspi
- name: expand file system
command: "raspi-config nonint do_expand_rootfs"
when: raspi_fs_expandable
tags:
- raspi

View File

@ -0,0 +1,221 @@
---
- name: get Raspberry Pi model type
shell: "raspi-config nonint get_pi_type"
register: raspi_type
changed_when: False
check_mode: no
tags:
- raspi
- name: get hostname via raspi-config
shell: "raspi-config nonint get_hostname"
register: raspi_hostname
changed_when: False
check_mode: no
tags:
- raspi
- name: get boot-to-gui setting
shell: "raspi-config nonint get_boot_cli" # 0 == "boot to cli"; 1 == "boot to gui"
register: raspi_boot_gui
changed_when: False
check_mode: no
tags:
- raspi
- name: get autologin setting
shell: "raspi-config nonint get_autologin" # 0 == "enable autologin"; 1 == "disable autologin"
register: raspi_noautologin
changed_when: False
check_mode: no
tags:
- raspi
- name: get wait-for-network-on-boot setting
shell: "raspi-config nonint get_boot_wait" # 0 == "wait"; 1 == "don't wait"
register: raspi_boot_nowait
changed_when: False
check_mode: no
tags:
- raspi
- name: get splash screen setting
shell: "raspi-config nonint get_boot_splash" # 0 == "wait"; 1 == "don't wait"
register: raspi_boot_splash
changed_when: False
check_mode: no
tags:
- raspi
- name: get wifi country
shell: "raspi-config nonint get_wifi_country"
register: raspi_wifi_country
changed_when: False
check_mode: no
tags:
- raspi
- name: get camera status
shell: "raspi-config nonint get_camera" # 0 == "camera enabled"; 1 == "camera disabled"
register: raspi_camera_disabled
changed_when: False
check_mode: no
tags:
- raspi
- name: get ssh enabled status
shell: "raspi-config nonint get_ssh"
register: raspi_ssh_disabled
changed_when: False
check_mode: no
tags:
- raspi
- name: get VNC enabled status
shell: "raspi-config nonint get_vnc"
register: raspi_vnc_disabled
changed_when: False
check_mode: no
tags:
- raspi
- name: get SPI enabled status
shell: "raspi-config nonint get_spi"
register: raspi_spi_disabled
changed_when: False
check_mode: no
tags:
- raspi
- name: get I2C enabled status
shell: "raspi-config nonint get_i2c"
register: raspi_i2c_disabled
changed_when: False
check_mode: no
tags:
- raspi
- name: get serial enabled status
shell: "raspi-config nonint get_serial"
register: raspi_serial_disabled
changed_when: False
check_mode: no
tags:
- raspi
- name: get hardware serial enabled status
shell: "raspi-config nonint get_serial_hw"
register: raspi_serial_hw_disabled
changed_when: False
check_mode: no
tags:
- raspi
- name: get onewire enabled status
shell: "raspi-config nonint get_onewire"
register: raspi_onewire_disabled
changed_when: False
check_mode: no
tags:
- raspi
- name: get remote gpio enabled status
shell: "raspi-config nonint get_rgpio"
register: raspi_rgpio_disabled
changed_when: False
check_mode: no
tags:
- raspi
- name: get overclock state
shell: "raspi-config nonint get_config_var arm_freq /boot/config.txt"
register: raspi_overclock
changed_when: False
check_mode: no
tags:
- raspi
- name: get fs expandability
shell: "raspi-config nonint get_can_expand"
register: raspi_fs_unexpandable
changed_when: False
check_mode: no
tags:
- raspi
- name: get overscan setting
shell: "raspi-config nonint get_overscan"
register: raspi_overscan
changed_when: False
check_mode: no
tags:
- raspi
- name: get GPU memory split
shell: "raspi-config nonint get_config_var gpu_mem /boot/config.txt"
register: raspi_gpu_mem
changed_when: False
check_mode: no
tags:
- raspi
- name: get GPU memory split 256
shell: "raspi-config nonint get_config_var gpu_mem_256 /boot/config.txt"
register: raspi_gpu_mem_256
changed_when: False
check_mode: no
tags:
- raspi
- name: get GPU memory split 512
shell: "raspi-config nonint get_config_var gpu_mem_512 /boot/config.txt"
register: raspi_gpu_mem_512
changed_when: False
check_mode: no
tags:
- raspi
- name: get GPU memory split 1024
shell: "raspi-config nonint get_config_var gpu_mem_1024 /boot/config.txt"
register: raspi_gpu_mem_1024
changed_when: False
check_mode: no
tags:
- raspi
- name: determine last allocated disk sector
shell: "parted /dev/mmcblk0 -ms unit s p | tail -n1 | awk -F':' '{ print $3 + 0 }'"
register: raspi_last_alloced_sector
changed_when: False
check_mode: no
tags:
- raspi
- name: set raspi-config facts
set_fact:
raspi_type: "{{ raspi_type.stdout }}"
raspi_hostname: "{{ raspi_hostname.stdout }}"
raspi_wifi_country: "{{ raspi_wifi_country.stdout }}"
raspi_gui_enabled: "{{ raspi_boot_gui.stdout != '0' }}"
raspi_autologin_enabled: "{{ raspi_noautologin.stdout == '0' }}"
raspi_bootwait_enabled: "{{ raspi_boot_nowait.stdout == '0' }}"
raspi_bootsplash_enabled: "{{ raspi_boot_splash.stdout == '0' }}"
raspi_camera_enabled: "{{ raspi_camera_disabled.stdout == '0' }}"
raspi_ssh_enabled: "{{ raspi_ssh_disabled.stdout == '0' }}"
raspi_vnc_enabled: "{{ raspi_vnc_disabled.stdout == '0' and 'find' not in raspi_vnc_disabled.stderr and 'found' not in raspi_vnc_disabled.stderr }}"
raspi_spi_enabled: "{{ raspi_spi_disabled.stdout == '0' }}"
raspi_i2c_enabled: "{{ raspi_i2c_disabled.stdout == '0' }}"
raspi_serial_enabled: "{{ raspi_serial_disabled.stdout == '0' }}"
raspi_serial_hw_enabled: "{{ raspi_serial_hw_disabled.stdout == '0' }}"
raspi_onewire_enabled: "{{ raspi_onewire_disabled.stdout == '0' }}"
raspi_rgpio_enabled: "{{ raspi_rgpio_disabled.stdout == '0' }}"
raspi_overclock: "{{ raspi_overclock.stdout }}"
raspi_fs_expandable: "{{ raspi_fs_unexpandable.stdout == '0' and (raspi_last_alloced_sector.stdout|int + 1) < ansible_devices.mmcblk0.sectors|int }}"
raspi_overscan: "{{ raspi_overscan.stdout }}"
raspi_gpu_mem: "{{ raspi_gpu_mem.stdout }}"
raspi_gpu_mem_256: "{{ raspi_gpu_mem_256.stdout }}"
raspi_gpu_mem_512: "{{ raspi_gpu_mem_512.stdout }}"
raspi_gpu_mem_1024: "{{ raspi_gpu_mem_1024.stdout }}"
check_mode: no
tags:
- raspi

View File

@ -0,0 +1,45 @@
---
# Uninstall Raspbian bloat
- name: remove raspbian bloat
apt:
name="{{ packages }}"
state=absent
vars:
packages:
- wolfram-engine
- libreoffice*
- scratch
- minecraft-pi
- python-minecraftpi
- python3-minecraftpi
- sonic-pi
- dillo
- gpiciew
- penguinspuzzle
tags:
- sw
# Install required software
- name: install basic software environment
apt:
name="{{ packages }}"
state=present
update_cache=yes
vars:
packages:
- vim
- git
- python-pip
- ufw
tags:
- sw
- name: install additional software
apt:
name="{{ myconfig.extra_software }}"
state=present
update_cache=yes
when: "'extra_software' in myconfig"
tags:
- sw

View File

@ -0,0 +1,41 @@
---
### Switch to non-default user as soon as possible if possible
#- name: does primary login user exist?
# local_action: "command ssh -q -o ConnectTimeout=3 -l {{ create_users[0].name }} {{ inventory_hostname }} /bin/true"
# register: user_exists
# ignore_errors: true
# changed_when: false
#
#- name: switch remote_user if possible
# remote_user: "{{ user_exists | success | ternary(omit, create_users[0].name) }}"
# command: "/bin/true"
# changed_when: false
### Create user accounts
- name: create users
user: name="{{ item.name }}"
comment="{{ item.comment }}"
group="{{ item.group }}"
groups="{{ item.groups }}"
uid="{{ item.uid }}"
state=present
shell=/bin/bash
with_items: "{{ create_users }}"
tags: [ 'users' ]
- name: install ssh pubkeys for new users
authorized_key: user="{{ item.name }}"
key="{{ item.pubkey }}"
state=present
with_items: "{{ create_users }}"
tags: [ 'users' ]
### disable the 'pi' user's ability to login in with password
### if you enable this, you may lock yourself out--you must make sure another
### user has been added with both sudo privileges and a password by which
### sudo can be authenticated
#- name: disable 'pi' user
# user: name="pi"
# password="*"
# state=present
# tags: [ 'users' ]

View File

@ -1,9 +1,46 @@
--- ---
### Hosts that must be present in /etc/hosts macaddrs:
etc_hosts_contents: b8:27:eb:39:d7:57:
- { name: 'clovermill', ip: '192.168.1.149' } hostname: "clovermine"
- { name: 'cloverfield', ip: '192.168.1.153' } domain: "local"
- { name: 'clovermine', ip: '192.168.1.154' } locale: "en_US.UTF-8"
timezone: "America/Los_Angeles"
xkblayout: "us"
wifi_country: "US"
enable_gui: True
enable_autologin: False
enable_bootwait: False
enable_bootsplash: False
enable_camera: False
enable_vnc: False
enable_spi: False
enable_i2c: False
enable_serial: True
enable_serial_hw: True
enable_onewire: False
enable_rgpio: False
b8:27:eb:ff:35:c7:
hostname: "cloverleaf"
domain: "local"
locale: "en_US.UTF-8"
timezone: "America/Los_Angeles"
xkblayout: "us"
enable_gui: False
enable_autologin: False
enable_bootwait: True
enable_bootsplash: False
enable_camera: False
enable_vnc: False
enable_spi: False
enable_i2c: False
enable_serial: True
enable_serial_hw: True
enable_onewire: False
enable_rgpio: False
extra_software:
- "w3m"
- "irssi"
- "screen"
### Users that must be present on the system ### Users that must be present on the system
create_users: create_users:

View File

@ -1,4 +0,0 @@
---
- hosts: all
roles:
- role: common