Changeset - 5bc6b7fb4cb5
[Not reviewed]
0 4 1
Branko Majic (branko) - 7 years ago 2017-11-19 18:45:32
branko@majic.rs
MAR-127: Implemented time synchronisation set-up in common role:

- Added new parameter ntp_servers for defining list of servers to use.
- Deploy ntp and ntpdate packages and relevant configuration files.
- Updated tests, fixing expected restriction lines in ntp
configuration, and adding an additional test to make sure the ntp
daemon has reread its configuration.
5 files changed with 94 insertions and 3 deletions:
0 comments (0 inline, 0 general)
roles/common/defaults/main.yml
Show inline comments
 
---
 

	
 
enable_backup: False
 
common_packages: []
 
os_users: []
 
os_groups: []
 
ca_certificates: {}
 
incoming_connection_limit: 3/second
 
incoming_connection_limit_burst: 9
 
prompt_colour: none
 
prompt_id: null
 
extra_backup_patterns:
 
  - "/root"
 
  - "/home"
 
pip_check_requirements:
 
  - click==6.7
 
  - first==2.0.1
 
  - pip-tools==1.9.0
 
  - six==1.10.0
 
ntp_servers: []
 

	
 
# Internal use only.
 
prompt_colour_mapping:
 
  black: "0;30"
 
  red: "0;31"
 
  green: "0;32"
 
  brown: "0;33"
 
  blue: "0;34"
 
  purple: "0;35"
 
  cyan: "0;36"
 
  light_gray: "0;37"
 
  dark_gray: "1;30"
 
  light_red: "1;31"
 
  light_green: "1;32"
 
  yellow: "1;33"
 
  light_blue: "1;34"
 
  light_purple: "1;35"
 
  light_cyan: "1;36"
 
  white: "1;37"
 
  none: "0"
 
\ No newline at end of file
 
  none: "0"
roles/common/handlers/main.yml
Show inline comments
 
---
 

	
 
- name: Update PAM configuration
 
  command: "/usr/sbin/pam-auth-update --package"
 
  tags:
 
    # [ANSIBLE0012] Commands should not change things if nothing needs doing
 
    #   This task is invoked only if user is very specific about requiring to
 
    #   run the handlers manually as a way to bring the system to consistency
 
    #   after interrupted runs.
 
    - skip_ansible_lint
 

	
 
- name: Restart SSH
 
  service:
 
    name: ssh
 
    state: restarted
 

	
 
- name: Update CA certificate cache
 
  command: "/usr/sbin/update-ca-certificates --fresh"
 
  tags:
 
    # [ANSIBLE0012] Commands should not change things if nothing needs doing
 
    #   This task is invoked only if user is very specific about requiring to
 
    #   run the handlers manually as a way to bring the system to consistency
 
    #   after interrupted runs.
 
    - skip_ansible_lint
 

	
 
- name: Restart ferm
 
  service:
 
    name: ferm
 
    state: restarted
 

	
 
# @TODO: Replace this with use of systemd module once Ansible is upgraded to
 
# version 2.2+.
 
- name: Reload systemd
 
  command: "systemctl daemon-reload"
 
  tags:
 
    # [ANSIBLE0012] Commands should not change things if nothing needs doing
 
    #   This task is invoked only if user is very specific about requiring to
 
    #   run the handlers manually as a way to bring the system to consistency
 
    #   after interrupted runs.
 
    - skip_ansible_lint
 

	
 
- name: Restart NTP server
 
  service:
 
    name: ntp
 
    state: restarted
roles/common/tasks/main.yml
Show inline comments
 
---
 

	
 
- name: Enable use of proxy for retrieving system packages via apt
 
  template:
 
    src: "apt_proxy.j2"
 
    dest: "/etc/apt/apt.conf.d/00proxy"
 
    owner: root
 
    group: root
 
    mode: 0644
 
  when: apt_proxy is defined
 

	
 
- name: Disable use of proxy for retrieving system packages via apt
 
  file:
 
    path: "/etc/apt/apt.conf.d/00proxy"
 
    state: absent
 
  when: apt_proxy is undefined
 

	
 
- name: Deploy pam-auth-update configuration file for enabling pam_umask
 
  copy:
 
    src: "pam_umask"
 
    dest: "/usr/share/pam-configs/umask"
 
    owner: root
 
    group: root
 
    mode: 0644
 
  notify:
 
    - Update PAM configuration
 

	
 
- name: Set login UMASK
 
  lineinfile:
 
    dest: "/etc/login.defs"
 
    state: present
 
    backrefs: yes
 
    regexp: '^UMASK(\s+)'
 
    line: 'UMASK\g<1>027'
 

	
 
- name: Set home directory mask
 
  lineinfile:
 
    dest: "/etc/adduser.conf"
 
    state: present
 
    backrefs: yes
 
    regexp: '^DIR_MODE='
 
    line: 'DIR_MODE=0750'
 

	
 
- name: Deploy bash profile configuration for fancier prompts
 
  template:
 
    src: "bash_prompt.sh.j2"
 
    dest: "/etc/profile.d/bash_prompt.sh"
 
    owner: root
 
    group: root
 
    mode: 0644
 

	
 
- name: Deploy profile configuration that allows for user-specific profile.d files
 
  copy:
 
    src: "user_profile_d.sh"
 
    dest: "/etc/profile.d/z99-user_profile_d.sh"
 
    owner: root
 
    group: root
 
    mode: 0644
 

	
 
- name: Replace default and skeleton bashrc
 
  copy:
 
    src: "{{ item.key }}"
 
    dest: "{{ item.value }}"
 
    owner: root
 
    group: root
 
    mode: 0644
 
  with_dict:
 
    bashrc: "/etc/bash.bashrc"
 
    skel_bashrc: "/etc/skel/.bashrc"
 

	
 
- name: Calculate stock checksum for bashrc root account
 
  stat:
 
    path: "/root/.bashrc"
 
  register: root_bashrc_stat
 

	
 
- name: Replace stock bashrc for root account with skeleton one
 
  copy:
 
    src: "skel_bashrc"
 
    dest: "/root/.bashrc"
 
    owner: root
 
    group: root
 
    mode: 0640
 
  when: root_bashrc_stat.stat.checksum == "b737c392222ddac2271cc8d0d8cc0308d08cf458"
 

	
 
- name: Install sudo
 
  apt:
 
    name: sudo
 
    state: present
 

	
 
- name: Install ssl-cert package
 
  apt:
 
    name: ssl-cert
 
    state: present
 

	
 
- name: Install rcconf (workaround for systemctl broken handling of SysV)
 
  apt:
 
    name: rcconf
 
    state: present
 

	
 
- name: Install common packages
 
  apt:
 
    name: "{{ item }}"
 
    state: "present"
 
  with_items: "{{ common_packages }}"
 

	
 
- name: Set-up MariaDB mysql_config symbolic link for compatibility (workaround for Debian bug 766996)
 
  file:
 
    src: "/usr/bin/mariadb_config"
 
    dest: "/usr/bin/mysql_config"
 
    state: link
 
  when: "'libmariadb-client-lgpl-dev-compat' in common_packages and ansible_distribution_release == 'jessie'"
 

	
 
- name: Disable electric-indent-mode for Emacs by default for all users
 
  copy:
 
    src: "01disable-electric-indent-mode.el"
 
    dest: "/etc/emacs/site-start.d/01disable-electric-indent-mode.el"
 
    owner: root
 
    group: root
 
    mode: 0644
 
  when: "'emacs24' in common_packages or 'emacs24-nox' in common_packages"
 

	
 
- name: Set-up operating system groups
 
  group:
 
    name: "{{ item.name }}"
 
    gid: "{{ item.gid | default(omit) }}"
 
    state: present
 
  with_items: "{{ os_groups }}"
 

	
 
- name: Set-up operating system user groups
 
  group:
 
    name: "{{ item.name }}"
 
    gid: "{{ item.uid | default(omit) }}"
 
    state: present
 
  with_items: "{{ os_users }}"
 

	
 
- name: Set-up operating system users
 
  user:
 
    name: "{{ item.name }}"
 
    uid: "{{ item.uid | default(omit) }}"
 
    group: "{{ item.name }}"
 
    groups: "{{ ','.join(item.additional_groups | default([])) }}"
 
    append: yes
 
    shell: /bin/bash
 
    state: present
 
    password: "{{ item.password | default('!') }}"
 
    update_password: on_create
 
  with_items: "{{ os_users }}"
 

	
 
- name: Set-up authorised keys
 
  authorized_key:
 
    user: "{{ item.0.name }}"
 
    key: "{{ item.1 }}"
 
  with_subelements:
 
    - "{{ os_users | selectattr('authorized_keys', 'defined') | list }}"
 
    - authorized_keys
 

	
 
- name: Disable remote logins for root
 
  lineinfile:
 
    dest: "/etc/ssh/sshd_config"
 
    state: present
 
    regexp: "^PermitRootLogin"
 
    line: "PermitRootLogin no"
 
  notify:
 
    - Restart SSH
 

	
 
- name: Disable remote login authentication via password
 
  lineinfile:
 
    dest: "/etc/ssh/sshd_config"
 
    state: present
 
    regexp: "^PasswordAuthentication"
 
    line: "PasswordAuthentication no"
 
  notify:
 
    - Restart SSH
 

	
 
- name: Deploy CA certificates
 
  copy:
 
    content: "{{ item.value }}"
 
    dest: "/usr/local/share/ca-certificates/{{ item.key }}.crt"
 
    owner: root
 
    group: root
 
    mode: 0644
 
  with_dict: "{{ ca_certificates }}"
 
  register: deploy_ca_certificates_result
 

	
 
- name: Update CA certificate cache
 
  command: "/usr/sbin/update-ca-certificates --fresh"
 
  when: deploy_ca_certificates_result.changed
 
  tags:
 
    # [ANSIBLE0016] Tasks that run when changed should likely be handlers
 
    #   CA certificate cache must be updated immediatelly in order for
 
    #   applications depending on deployed CA certificates can use them to
 
    #   validate server/client certificates.
 
    - skip_ansible_lint
 

	
 
- name: Install ferm (for firewall management)
 
  apt:
 
    name: ferm
 
    state: installed
 

	
 
- name: Configure ferm init script coniguration file
 
  copy:
 
    src: "ferm"
 
    dest: "/etc/default/ferm"
 
    owner: root
 
    group: root
 
    mode: 0644
 
  notify:
 
    - Restart ferm
 

	
 
- name: Create directory for storing ferm configuration files
 
  file:
 
    dest: "/etc/ferm/conf.d/"
 
    state: directory
 
    owner: root
 
    group: root
 
    mode: 0750
 

	
 
- name: Deploy main ferm configuration file
 
  copy:
 
    src: "ferm.conf"
 
    dest: "/etc/ferm/ferm.conf"
 
    owner: root
 
    group: root
 
    mode: 0640
 
  notify:
 
    - Restart ferm
 

	
 
- name: Deploy ferm base rules
 
  template:
 
    src: "00-base.conf.j2"
 
    dest: "/etc/ferm/conf.d/00-base.conf"
 
    owner: root
 
    group: root
 
    mode: 0640
 
  notify:
 
    - Restart ferm
 

	
 
- name: Enable ferm service on boot (workaround for systemctl broken handling of SysV)
 
  command: "rcconf -on ferm"
 
  register: result
 
  changed_when: result.stderr == ""
 

	
 
- name: Enable ferm service
 
  service:
 
    name: ferm
 
    state: started
 

	
 
- name: Deploy script for validating server certificates
 
  copy:
 
    src: "check_certificate.sh"
 
    dest: "/usr/local/bin/check_certificate.sh"
 
    owner: root
 
    group: root
 
    mode: 0755
 

	
 
- name: Set-up directory for holding configuration for certificate validation script
 
  file:
 
    path: "/etc/check_certificate"
 
    state: "directory"
 
    owner: root
 
    group: root
 
    mode: 0755
 

	
 
- name: Deploy crontab entry for checking certificates
 
  cron:
 
    name: "check_certificate"
 
    cron_file: "check_certificate"
 
    hour: 0
 
    minute: 0
 
    job: "/usr/local/bin/check_certificate.sh expiration"
 
    state: present
 
    user: nobody
 

	
 
- name: Install apticron (for checking available upgrades)
 
  apt:
 
    name: apticron
 
    state: installed
 

	
 
# Implementation for checking pip requirements files via via pip-tools.
 
- name: Install virtualenv for pip requirements checks
 
  apt:
 
    name: virtualenv
 
    state: installed
 

	
 
- name: Create dedicated group for user running pip requirements checks
 
  group:
 
    name: "pipreqcheck"
 
    gid: "{{ pipreqcheck_gid | default(omit) }}"
 
    state: present
 

	
 
- name: Create user for running pip requirements checks
 
  user:
 
    name: "pipreqcheck"
 
    uid: "{{ pipreqcheck_uid | default(omit) }}"
 
    group: "pipreqcheck"
 
    home: "/var/lib/pipreqcheck"
 
    state: present
 

	
 
- name: Create directory for Python virtual environment used for installing/running pip-tools
 
  file:
 
    path: "/var/lib/pipreqcheck/virtualenv"
 
    state: directory
 
    owner: pipreqcheck
 
    group: pipreqcheck
 
    mode: 0750
 

	
 
- name: Create Python virtual environment used for installing/running pip-tools
 
  command: "/usr/bin/virtualenv --prompt '(pipreqcheck)' '/var/lib/pipreqcheck/virtualenv'"
 
  args:
 
     creates: '/var/lib/pipreqcheck/virtualenv/bin/activate'
 
  become: yes
 
  become_user: "pipreqcheck"
 
  tags:
 
    # [ANSIBLE0012] Commands should not change things if nothing needs doing
 
    #   Command will not run if the virtualenv has already been created,
 
    #   therefore the warning is a false positive.
 
    - skip_ansible_lint
 

	
 
- name: Create directory for storing pip requirements files
 
  file:
 
    path: "/etc/pip_check_requirements_upgrades"
 
    state: "directory"
 
    owner: root
 
    group: pipreqcheck
 
    mode: 0750
 

	
 
- name: Set-up directory for storing pip requirements file for pip-tools virtual environment itself
 
  file:
 
    path: "/etc/pip_check_requirements_upgrades/pipreqcheck"
 
    state: "directory"
 
    owner: root
 
    group: pipreqcheck
 
    mode: 0750
 

	
 
- name: Deploy .in file for pip requirements in pip-tools virtual environment
 
  copy:
 
    src: "pipreqcheck_requirements.in"
 
    dest: "/etc/pip_check_requirements_upgrades/pipreqcheck/requirements.in"
 
    owner: root
 
    group: pipreqcheck
 
    mode: 0640
 

	
 
- name: Deploy requirements file for pipreqcheck virtual environment
 
  template:
 
    src: "pipreqcheck_requirements.txt.j2"
 
    dest: "/etc/pip_check_requirements_upgrades/pipreqcheck/requirements.txt"
 
    owner: root
 
    group: pipreqcheck
 
    mode: 0640
 

	
 
- name: Install latest pip in pip-tools virtual environment
 
  pip:
 
    name:
 
      - "pip>=9.0.0,<10.0.0"
 
    virtualenv: "~pipreqcheck/virtualenv"
 
  become: yes
 
  become_user: "pipreqcheck"
 

	
 
- name: Install pip-tools if not present
 
  pip:
 
    name: pip-tools
 
    state: present
 
    virtualenv: "~pipreqcheck/virtualenv"
 
  become: yes
 
  become_user: "pipreqcheck"
 

	
 
- name: Synchronise pip-tools virtual environment via deployed requirements file
 
  shell: "source ~pipreqcheck/virtualenv/bin/activate && pip-sync /etc/pip_check_requirements_upgrades/pipreqcheck/requirements.txt"
 
  args:
 
    executable: /bin/bash
 
  become: yes
 
  become_user: "pipreqcheck"
 
  register: pipreqcheck_pip_sync
 
  changed_when: "pipreqcheck_pip_sync.stdout != 'Everything up-to-date'"
 

	
 
- name: Deploy script for checking available upgrades
 
  copy:
 
    src: "pip_check_requirements_upgrades.sh"
 
    dest: "/usr/local/bin/pip_check_requirements_upgrades.sh"
 
    owner: root
 
    group: root
 
    mode: 0755
 

	
 
- name: Deploy crontab entry for checking pip requirements
 
  copy:
 
    src: "cron_check_pip_requirements"
 
    dest: "/etc/cron.d/check_pip_requirements"
 
    owner: root
 
    group: root
 
    mode: 0644
 

	
 
- name: Install NTP packages
 
  apt:
 
    name:
 
      - ntp
 
      - ntpdate
 
    state: installed
 
  when: ntp_servers
 

	
 
- name: Deploy NTP configuration
 
  template:
 
    src: "ntp.conf.j2"
 
    dest: "/etc/ntp.conf"
 
    owner: root
 
    group: root
 
    mode: 0644
 
  when: ntp_servers
 
  notify:
 
    - Restart NTP server
 

	
 
- name: Explicitly run all handlers
 
  include: ../handlers/main.yml
 
  when: "handlers | default(False) | bool() == True"
 
  tags:
 
    - handlers
roles/common/templates/ntp.conf.j2
Show inline comments
 
new file 100644
 
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
 

	
 
driftfile /var/lib/ntp/ntp.drift
 

	
 

	
 
# Enable this if you want statistics to be logged.
 
#statsdir /var/log/ntpstats/
 

	
 
statistics loopstats peerstats clockstats
 
filegen loopstats file loopstats type day enable
 
filegen peerstats file peerstats type day enable
 
filegen clockstats file clockstats type day enable
 

	
 

	
 
# You do need to talk to an NTP server or two (or three).
 
#server ntp.your-provider.example
 

	
 
# pool.ntp.org maps to about 1000 low-stratum NTP servers.  Your server will
 
# pick a different set every time it starts up.  Please consider joining the
 
# pool: <http://www.pool.ntp.org/join.html>
 
{% for server in ntp_servers %}
 
server {{ server }} iburst
 
{% endfor %}
 

	
 
# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
 
# details.  The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
 
# might also be helpful.
 
#
 
# Note that "restrict" applies to both servers and clients, so a configuration
 
# that might be intended to block requests from certain clients could also end
 
# up blocking replies from your own upstream servers.
 

	
 
# By default, exchange time with everybody, but don't allow configuration.
 
restrict -4 default kod notrap nomodify nopeer noquery
 
restrict -6 default kod notrap nomodify nopeer noquery
 

	
 
# Local users may interrogate the ntp server more closely.
 
restrict 127.0.0.1
 
restrict ::1
 

	
 
# Clients from this (example!) subnet have unlimited access, but only if
 
# cryptographically authenticated.
 
#restrict 192.168.123.0 mask 255.255.255.0 notrust
 

	
 

	
 
# If you want to provide time to your local subnet, change the next line.
 
# (Again, the address is an example only.)
 
#broadcast 192.168.123.255
 

	
 
# If you want to listen to time broadcasts on your local subnet, de-comment the
 
# next lines.  Please do this only if you trust everybody on the network!
 
#disable auth
 
#broadcastclient
roles/common/tests/test_parameters_optional.py
Show inline comments
 
import os
 
import re
 
import socket
 

	
 
import paramiko
 

	
 
import testinfra.utils.ansible_runner
 

	
 

	
 
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
 
    '.molecule/ansible_inventory').get_hosts('parameters-optional')
 

	
 

	
 
def test_apt_proxy(File):
 
    """
 
    Tests if proxy configuration for apt has been deployed correctly.
 
    """
 

	
 
    proxy_config = File('/etc/apt/apt.conf.d/00proxy')
 

	
 
    assert proxy_config.exists
 
    assert proxy_config.user == 'root'
 
    assert proxy_config.group == 'root'
 
    assert proxy_config.mode == 0o644
 

	
 

	
 
def test_bash_prompt_content(File):
 
    """
 
    Tests that custom bash prompt has been configured correctly with specified
 
    colour and prompt.
 
    """
 

	
 
    config = File('/etc/profile.d/bash_prompt.sh')
 

	
 
    assert "export PS1='\\[\\e]0;\\u@\\h: \\w\\a\\]${debian_chroot:+($debian_chroot)}\\[\\033[0;36m\\]\\u@\\h[test]:\\w\\$ \\[\\033[0m\\]'" in config.content
 
    assert "export PS1='\\[\\e]0;\\u@\\h: \\w\\a\\]${debian_chroot:+($debian_chroot)}\\u@\\h[test]:\\w\\$ '" in config.content
 

	
 

	
 
def test_common_installed_packages_common(Ansible, Package):
 
    """
 
    Tests that user-provided common packages have been installed.
 
    """
 

	
 
    debian_release = Ansible("setup")["ansible_facts"]["ansible_distribution_release"]
 

	
 
    assert Package('units').is_installed
 
    assert Package('gnutls-bin').is_installed
 

	
 
    # Different name of package in different Debian releases.
 
    if debian_release == 'jessie':
 
        assert Package('libmariadb-client-lgpl-dev-compat').is_installed
 
    elif debian_release == 'stretch':
 
        assert Package('libmariadbclient-dev-compat').is_installed
 
    else:
 
        raise Exception("Cannot run this test on debian release: %s" % debian_release)
 

	
 

	
 
def test_ssh_login_mechanisms():
 
    """
 
    Tests available SSH login mechanisms (should be just public key).
 
    """
 

	
 
    sock = socket.socket()
 
    sock.connect(('10.31.127.4', 22))
 

	
 
    transport = paramiko.transport.Transport(sock)
 
    transport.connect()
 

	
 
    try:
 
        transport.auth_none('')
 
    except paramiko.transport.BadAuthenticationType, err:
 
        assert err.allowed_types == ['publickey']
 

	
 

	
 
def test_mariadb_mysql_config_symlink(Ansible, File):
 
    """
 
    Tests if symbolic link has been set-up for mariadb_config binary to be
 
    accessible as mysql_config as well.
 

	
 
    Only applicable to Debian Jessie.
 
    """
 

	
 
    if Ansible("setup")["ansible_facts"]["ansible_distribution_release"] == 'jessie':
 
        mysql_config = File('/usr/bin/mysql_config')
 

	
 
        assert mysql_config.is_symlink
 
        assert mysql_config.linked_to == '/usr/bin/mariadb_config'
 

	
 

	
 
def test_emacs_electric_indent_mode(File):
 
    """
 
    Tests if Emacs electric indent mode has been disabled via custom
 
    configuration file.
 
    """
 

	
 
    emacs_config = File('/etc/emacs/site-start.d/01disable-electric-indent-mode.el')
 

	
 
    assert emacs_config.is_file
 
    assert emacs_config.user == 'root'
 
    assert emacs_config.group == 'root'
 
    assert emacs_config.mode == 0o644
 
    assert "(electric-indent-mode -1)" in emacs_config.content
 

	
 

	
 
def test_os_groups(Group):
 
    """
 
    Tests if user-supplied system groups have been created correctly.
 
    """
 

	
 
    group1 = Group('group1')
 
    assert group1.gid == 1001
 

	
 
    group2 = Group('group2')
 
    assert group2.gid == 3001
 

	
 
    group3 = Group('group3')
 
    assert group3.gid == 3002
 

	
 
    user1_group = Group('user1')
 
    assert user1_group.gid == 3003
 

	
 
    user2_group = Group('user2')
 
    assert user2_group.gid == 2001
 

	
 
    user3_group = Group('user3')
 
    assert user3_group.gid == 2002
 

	
 

	
 
def test_os_users(File, Sudo, User):
 
    """
 
    Tests if user-supplied system users have been created correctly.
 
    """
 

	
 
    with Sudo():
 
        user1 = User('user1')
 
        assert user1.uid == 1001
 
        assert user1.group == 'user1'
 
        assert user1.groups == ['user1']
 
        assert user1.shell == '/bin/bash'
 
        assert user1.password == '!'
 

	
 
        user1_authorized_keys = File(os.path.join(user1.home, '.ssh', 'authorized_keys'))
 
        assert not user1_authorized_keys.exists
 

	
 
        user2 = User('user2')
 
        assert user2.uid == 2001
 
        assert user2.group == 'user2'
 
        assert sorted(user2.groups) == sorted(['group1', 'group2', 'user2'])
 
        assert user2.shell == '/bin/bash'
 
        assert user2.password == '$6$wdXOQiMe09ugh0$VRIph2XA2QQyEYlAlH7zT4TPACDUalf/4FKpqG9JRHfKxANTcTug2ANCt450htcs0LikJfHLWofLP54jraFU61'
 

	
 
        user2_authorized_keys = File(os.path.join(user2.home, '.ssh', 'authorized_keys'))
 
        assert open('tests/data/ssh/clientkey1.pub', 'r').read().strip() in user2_authorized_keys.content
 
        assert open('tests/data/ssh/clientkey2.pub', 'r').read().strip() in user2_authorized_keys.content
 

	
 
        user3 = User('user3')
 
        assert user3.uid == 2002
 
        assert user3.group == 'user3'
 
        assert sorted(user3.groups) == sorted(['group3', 'user3'])
 
        assert user3.shell == '/bin/bash'
 
        assert user3.password == '$6$nmx.21uLqT$9LrUqNUgUwIM.l0KFKgr2.kDEwe2lo7IbBIhnG70AGW7GTFdWBUFnGAxH15YxikTXhDJD/uxd.NNgojEOjRvx1'
 

	
 
        user3_authorized_keys = File(os.path.join(user3.home, '.ssh', 'authorized_keys'))
 
        assert open('tests/data/ssh/clientkey3.pub', 'r').read().strip() in user3_authorized_keys.content
 

	
 

	
 
def test_authorized_keys_login():
 
    """
 
    Tests if authorized SSH keys for user-provided system users have been set-up
 
    correctly.
 
    """
 

	
 
    client = paramiko.client.SSHClient()
 
    client.set_missing_host_key_policy(paramiko.client.WarningPolicy())
 

	
 
    # No exception will be raised if connection is successful.
 
    client.connect("10.31.127.4", username="user2", allow_agent=False, look_for_keys=False, key_filename='tests/data/ssh/clientkey1')
 
    client.connect("10.31.127.4", username="user2", allow_agent=False, look_for_keys=False, key_filename='tests/data/ssh/clientkey2')
 
    client.connect("10.31.127.4", username="user3", allow_agent=False, look_for_keys=False, key_filename='tests/data/ssh/clientkey3')
 

	
 

	
 
def test_ca_certificates(File):
 
    """
 
    Tests if CA certificates have been correctly deployed to the system.
 
    """
 

	
 
    ca1_cert = File('/usr/local/share/ca-certificates/cacert1.crt')
 
    assert ca1_cert.is_file
 
    assert ca1_cert.user == 'root'
 
    assert ca1_cert.group == 'root'
 
    assert ca1_cert.mode == 0o644
 

	
 
    ca1_cert_symlink = File('/etc/ssl/certs/cacert1.pem')
 
    assert ca1_cert_symlink.is_symlink
 
    assert ca1_cert_symlink.linked_to == '/usr/local/share/ca-certificates/cacert1.crt'
 

	
 
    ca1_cert_hash_1 = File('/etc/ssl/certs/3ce70b58.0')
 
    assert ca1_cert_hash_1.is_symlink
 
    assert ca1_cert_hash_1.linked_to == '/usr/local/share/ca-certificates/cacert1.crt'
 

	
 
    ca1_cert_hash_1 = File('/etc/ssl/certs/49f72a44.0')
 
    assert ca1_cert_hash_1.is_symlink
 
    assert ca1_cert_hash_1.linked_to == '/usr/local/share/ca-certificates/cacert1.crt'
 

	
 
    ca2_cert = File('/usr/local/share/ca-certificates/cacert2.crt')
 
    assert ca2_cert.is_file
 
    assert ca2_cert.user == 'root'
 
    assert ca2_cert.group == 'root'
 
    assert ca2_cert.mode == 0o644
 

	
 
    ca2_cert_symlink = File('/etc/ssl/certs/cacert2.pem')
 
    assert ca2_cert_symlink.is_symlink
 
    assert ca2_cert_symlink.linked_to == '/usr/local/share/ca-certificates/cacert2.crt'
 

	
 
    ca2_cert_hash_1 = File('/etc/ssl/certs/a52eec00.0')
 
    assert ca2_cert_hash_1.is_symlink
 
    assert ca2_cert_hash_1.linked_to == '/usr/local/share/ca-certificates/cacert2.crt'
 

	
 
    ca2_cert_hash_1 = File('/etc/ssl/certs/a0d2e9e4.0')
 
    assert ca2_cert_hash_1.is_symlink
 
    assert ca2_cert_hash_1.linked_to == '/usr/local/share/ca-certificates/cacert2.crt'
 

	
 

	
 
def test_ferm_base_rules(Command, File, Sudo):
 
    """
 
    Tests if base ferm configuration has been deployed correctly with proper
 
    user-provided rate-limiting.
 
    """
 

	
 
    with Sudo():
 
        ferm_base = File('/etc/ferm/conf.d/00-base.conf')
 

	
 
        assert "mod hashlimit hashlimit 5/second hashlimit-burst 5" in ferm_base.content
 

	
 
        iptables = Command('iptables-save')
 

	
 
        assert iptables.rc == 0
 
        assert "-A flood -p icmp -m icmp --icmp-type 8 -m hashlimit --hashlimit-upto 5/sec --hashlimit-burst 5 " \
 
            "--hashlimit-mode srcip --hashlimit-name icmp -j RETURN" in iptables.stdout
 
        assert "-A flood -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m hashlimit --hashlimit-upto 5/sec --hashlimit-burst 5 " \
 
            "--hashlimit-mode srcip --hashlimit-name icmp -j RETURN" in iptables.stdout
 

	
 
        ip6tables = Command('ip6tables-save')
 
        assert ip6tables.rc == 0
 
        assert "-A flood -p icmp -m icmp --icmp-type 8 -m hashlimit --hashlimit-upto 5/sec --hashlimit-burst 5 " \
 
            "--hashlimit-mode srcip --hashlimit-name icmp -j RETURN" in iptables.stdout
 
        assert "-A flood -p ipv6-icmp -m icmp6 --icmpv6-type 128 -m hashlimit --hashlimit-upto 5/sec --hashlimit-burst 5 " \
 
            "--hashlimit-mode srcip --hashlimit-name icmp -j RETURN" in ip6tables.stdout
 

	
 

	
 
def test_pipreqcheck_virtualenv_user(Group, User):
 
    """
 
    Tests if group and user for running pip requirements upgrade checks have
 
    been created correctly with user-provided uid/gid.
 
    """
 

	
 
    group = Group('pipreqcheck')
 
    assert group.exists
 
    assert group.gid == 2500
 

	
 
    user = User('pipreqcheck')
 
    assert user.exists
 
    assert user.home == '/var/lib/pipreqcheck'
 
    assert user.uid == 2500
 
    assert user.group == 'pipreqcheck'
 
    assert user.groups == ['pipreqcheck']
 

	
 

	
 
def test_backup_configuration(File, Sudo):
 
    """
 
    Tests if backup configuration has been deployed correctly.
 
    """
 

	
 
    with Sudo():
 

	
 
        common = File('/etc/duply/main/patterns/common')
 
        assert common.is_file
 
        assert "/var/log" in common.content.split("\n")
 
        assert "/etc/shadow" in common.content.split("\n")
 
        assert "/var/mail" in common.content.split("\n")
 
        assert "/var/spool/cron" in common.content.split("\n")
 

	
 
        common_extra = File('/etc/duply/main/patterns/common_extra')
 
        assert common_extra.is_file
 
        assert "/home/user1" in common_extra.content.split("\n")
 
        assert "/home/user2" in common_extra.content.split("\n")
 

	
 

	
 
def test_ntp_software_installed(Package):
 
    """
 
    Tests if NTP packages are installed.
 
    """
 

	
 
    assert Package('ntp').is_installed
 
    assert Package('ntpdate').is_installed
 

	
 

	
 
def test_ntp_server_configuration(File, Sudo):
 
    """
 
    Tests if NTP server has been correctly configured.
 
    """
 

	
 
    with Sudo():
 

	
 
        # Read the configuration file.
 
        configuration = File("/etc/ntp.conf").content.split("\n")
 

	
 
        # Extract only the relevant sections of files (exculde empty
 
        # lines and comments).
 
        configuration = [c.strip() for c in configuration if re.match('^\s*(|#.*)$', c) is None]
 

	
 
        # Ensure correct servers have been configured in the pool.
 
        servers = [c for c in configuration if c.startswith('server')]
 

	
 
        expected_servers = ["server 0.debian.pool.ntp.org iburst",
 
                            "server 1.debian.pool.ntp.org iburst",
 
                            "server 2.debian.pool.ntp.org iburst"]
 

	
 
        assert sorted(servers) == sorted(expected_servers)
 

	
 
        # Ensure querying of server is disable for untrusted clients.
 
        restrictions = [c for c in configuration if c.startswith('restrict')]
 
        expected_restrictions = ["restrict -4 default kod notrap nomodify nopeer noquery notrust",
 
                                 "restrict -6 default kod notrap nomodify nopeer noquery notrust"]
 
        expected_restrictions = ["restrict -4 default kod notrap nomodify nopeer noquery",
 
                                 "restrict -6 default kod notrap nomodify nopeer noquery",
 
                                 "restrict 127.0.0.1",
 
                                 "restrict ::1"]
 

	
 
        assert sorted(restrictions) == sorted(expected_restrictions)
 

	
 

	
 
def test_ntp_query_server_count(Command):
 

	
 
    # Two lines for headers, and one line per configured server.
 
    expected_stdout_line_count = 5
 

	
 
    ntpq = Command("ntpq -p -n")
 

	
 
    assert ntpq.rc == 0
 
    assert len(ntpq.stdout.split("\n")) == expected_stdout_line_count
 

	
 

	
 
def test_ntp_listening_interfaces(Socket):
 
    """
 
    Tests if NTP server is listening on correct ports.
 
    """
 

	
 
    assert Socket('udp://:::123').is_listening
0 comments (0 inline, 0 general)