diff --git a/.ansible-lint b/.ansible-lint
new file mode 100644
index 0000000000000000000000000000000000000000..e008c8fbeb56f1793e1153f56a773ad0896a83e2
--- /dev/null
+++ b/.ansible-lint
@@ -0,0 +1,89 @@
+---
+# based on documentation available at
+# https://ansible-lint.readthedocs.io/en/latest/configuring/
+
+# exclude_paths included in this file are parsed relative to this file's location
+# and not relative to the CWD of execution. CLI arguments passed to the --exclude
+# option will be parsed relative to the CWD of execution.
+exclude_paths:
+  - .cache/  # implicit unless exclude_paths is defined in config
+  - .git/
+  - .githooks/
+  - backups/
+# parseable: true
+# quiet: true
+# verbosity: 1
+
+# Mock modules or roles in order to pass ansible-playbook --syntax-check
+# mock_modules:
+#   - zuul_return
+#   # note the foo.bar is invalid as being neither a module or a collection
+#   - fake_namespace.fake_collection.fake_module
+#   - fake_namespace.fake_collection.fake_module.fake_submodule
+# mock_roles:
+#   - mocked_role
+#   - author.role_name # old standalone galaxy role
+#   - fake_namespace.fake_collection.fake_role # role within a collection
+
+# Enable checking of loop variable prefixes in roles
+loop_var_prefix: "{role}_"
+
+# Enforce variable names to follow pattern below, in addition to Ansible own
+# requirements, like avoiding python identifiers. To disable add `var-naming`
+# to skip_list.
+# var_naming_pattern: "^[a-z_][a-z0-9_]*$"
+
+use_default_rules: true
+# Load custom rules from this specific folder
+# rulesdir:
+#   - ./rule/directory/
+
+# This makes linter to fully ignore rules/tags listed below
+skip_list:
+  - skip_this_tag
+  - git-latest
+
+# Any rule that has the 'opt-in' tag will not be loaded unless its 'id' is
+# mentioned in the enable_list:
+enable_list:
+  - empty-string-compare  # opt-in
+  - no-log-password  # opt-in
+  - no-same-owner  # opt-in
+  # add yaml here if you want to avoid ignoring yaml checks when yamllint
+  # library is missing. Normally its absence just skips using that rule.
+  - yaml
+# Report only a subset of tags and fully ignore any others
+# tags:
+#   - var-spacing
+
+# This makes the linter display but not fail for rules/tags listed below:
+warn_list:
+  - skip_this_tag
+  - git-latest
+  - experimental  # experimental is included in the implicit list
+  # - role-name
+
+# Offline mode disables installation of requirements.yml
+offline: false
+
+# Define required Ansible's variables to satisfy syntax check
+# extra_vars:
+#   foo: bar
+#   multiline_string_variable: |
+#     line1
+#     line2
+#   complex_variable: ":{;\t$()"
+
+# Uncomment to enforce action validation with tasks, usually is not
+# needed as Ansible syntax check also covers it.
+# skip_action_validation: false
+
+# List of additional kind:pattern to be added at the top of the default
+# match list, first match determines the file kind.
+kinds:
+  # - playbook: "**/examples/*.{yml,yaml}"
+  # - galaxy: "**/folder/galaxy.yml"
+  # - tasks: "**/tasks/*.yml"
+  # - vars: "**/vars/*.yml"
+  # - meta: "**/meta/main.yml"
+  - yaml: "**/*.yaml-too"
diff --git a/.githooks/pre-commit b/.githooks/pre-commit
index 39148cfc5bc90d6d22f6acbad18f26e6b26771bf..b5975f0886faa4d654c04415bdcdb5dbf875747b 100755
--- a/.githooks/pre-commit
+++ b/.githooks/pre-commit
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 #
 # An example hook script to verify what is about to be committed.
 # Called by "git commit" with no arguments.  The hook should
@@ -103,7 +103,7 @@ echo "SUCCESS: URL detection stage."
 
 ### IP address detection stage
 # This is pretty basic regex matching, but it's a start.
-IP_REGEX='[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'
+IP_REGEX='[^a-zA-ZäöÜÄÖÜß/\\\-][0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'
 ${GREP_CMD} ${GREP_EXCLUDES} -e "${IP_REGEX}" "${REPOPATH}" | grep -v "127.0.0"
 if [[ ${?} -eq 0 ]]; then
 	echo "ERROR: found IP address."
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b4e785871981a4b0c74c4a777505a4a15c7e95bf
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,32 @@
+# A pipeline is composed of independent jobs that run scripts, grouped into stages.
+# Stages run in sequential order, but jobs within stages run in parallel.
+#
+# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
+
+stages:          # List of stages for jobs, and their order of execution
+  - test
+
+default:
+  before_script:
+    - source /opt/molecule/bin/activate
+    - ansible --version
+    - molecule --version
+
+test-job:
+  stage: test
+  tags:
+    - "shell"
+  script:
+    # make sure that Ansible Vaults are present and can be decrypted
+    - echo "${VAULT_LZA_PROXY}" > ../lza_proxy.pass
+    - export ANSIBLE_VAULT_PASSWORD_FILE=../lza_proxy.pass
+    - rm -rf ../ansible_vaults/
+    - git clone https://gitlab+deploy-token-25:${VAULT_ACCESS_TOKEN}@git.slub-dresden.de/slub-referat-2-3/ansible_vaults.git ../ansible_vaults/; \
+    # run Molecule tests
+    - molecule syntax --scenario-name default
+    - molecule lint --scenario-name default
+    - molecule create --scenario-name default
+    - molecule converge --scenario-name default
+    - molecule idempotence --scenario-name default
+      #- molecule verify --scenario-name default
+    - molecule destroy --scenario-name default
diff --git a/ansible.cfg b/ansible.cfg
index 2bcf54899be2296795593e4c8ce8db07c71e3159..506be4150e1f78e156ab398560ba71476dc193f9 100644
--- a/ansible.cfg
+++ b/ansible.cfg
@@ -1,7 +1,8 @@
 [defaults]
 # If set, configures the path to the Vault password file as an alternative to
 # specifying --vault-password-file on the command line.
-vault_identity_list = ../lza_install_common.pass, ../slub_osquery.pass, ../lza_server_hardening.pass, ../lza_proxy.pass
+# vault_identity_list = ../lza_install_common.pass, ../slub_osquery.pass, ../lza_server_hardening.pass, ../lza_proxy.pass
+vault_identity_list = ../lza_proxy.pass
 
 # Path to default inventory file
 # Administrators can override this by using the "-i <inventoryfile>" CLI
diff --git a/handlers/main.yml b/handlers/main.yml
index 3a13471182d3f1348521d76c836140a8ac7f411e..db701ee0aaf53e6b673972fb283feefe03c24935 100644
--- a/handlers/main.yml
+++ b/handlers/main.yml
@@ -1,6 +1,6 @@
 ---
 - name: Ordner für iptables-Config erstellen
-  file:
+  ansible.builtin.file:
     path: "/etc/iptables"
     state: directory
     owner: "root"
@@ -9,16 +9,16 @@
   listen: "save iptables rules"
 
 - name: install netfilter-persistent to be able to save iptables rules
-  apt:
+  ansible.builtin.apt:
     name: "netfilter-persistent"
     state: present
   listen: "save iptables rules"
 
 - name: save iptables rules
-  command: 'netfilter-persistent save'
+  ansible.builtin.command: 'netfilter-persistent save'
   listen: "save iptables rules"
 
 - name: restart squid proxy
-  systemd:
+  ansible.builtin.systemd:
     name: "squid.service"
     state: restarted
diff --git a/meta/main.yml b/meta/main.yml
index 8e5c5e115319e6e4ac43c351138026e1ab8d9068..3513b054b8f6fb4042bd21c8fc1b4c2be1ec8ee6 100644
--- a/meta/main.yml
+++ b/meta/main.yml
@@ -3,10 +3,11 @@ galaxy_info:
   author: Jörg Sachse
   description: role to install Squid caching proxy server for the SLUBarchiv digital preservation repository
   company: SLUB Dresden
+  namespace: slub
   # If the issue tracker for your role is not on github, uncomment the next line and provide a value issue_tracker_url: http://example.com/issue/tracker Some suggested licenses: - BSD
   # (default) - MIT - GPLv2 - GPLv3 - Apache - CC-BY
   license: public domain
-  min_ansible_version: 2.4
+  min_ansible_version: "2.4"
   # If this a Container Enabled role, provide the minimum Ansible Container version. min_ansible_container_version: Optionally specify the branch Galaxy will use when accessing the GitHub repo
   # for this role. During role install, if no tags are available, Galaxy will use this branch. During import Galaxy will access files on this branch. If Travis integration is configured, only
   # notifications for this branch will be accepted. Otherwise, in all cases, the repo's default branch (usually master) will be used. github_branch:
@@ -19,8 +20,7 @@ galaxy_info:
   platforms:
     - name: Debian
       versions:
-        - 9
-        - 10
+        - all
   galaxy_tags: []
   # List tags for your role here, one per line. A tag is a keyword that describes and categorizes the role. Users find roles by searching for tags. Be sure to remove the '[]' above, if you
   # add tags to this list.
diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml
deleted file mode 100644
index 0388ae009a7550fc14ecd4eceb9829a9d16ef66f..0000000000000000000000000000000000000000
--- a/molecule/default/converge.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-- name: Converge
-  hosts: all
-  roles:
-    - {role: "ansible_lza_proxy", become: true}
diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml
index 4dd6fd7a1e05cba5df6fb0e41628083caba68ce1..5424d3e9c29063b8594155baf5696d04e56fb16a 100644
--- a/molecule/default/molecule.yml
+++ b/molecule/default/molecule.yml
@@ -3,34 +3,17 @@ dependency:
   name: galaxy
 driver:
   name: vagrant
-  provider:
-    name: virtualbox
-lint: |
-  set -e
-  yamllint .
-  ansible-lint -x formatting
-  flake8 --ignore=E501
 platforms:
-  - name: molecule-test-proxy
-    box: debian/buster64
-    memory: 512
-    cpus: 1
+  - name: vm-runner
+    box: debian/bullseye64
+    memory: 1024
 provisioner:
   name: ansible
-  log: true
-  lint:
-    name: ansible-lint
-    enabled: false
-  config_options:
-    defaults:
-      vault_identity_list: "@$HOME/.ansible/roles/molecule_prepare.pass, @$HOME/.ansible/roles/lza_install_common.pass, @$HOME/.ansible/roles/lza_server_hardening.pass, @$HOME/.ansible/roles/lza_proxy.pass"
-  vvv: false
+  playbooks:
+    # create: ../resources/playbooks/create.yml
+    # destroy: ../resources/playbooks/destroy.yml
+    converge: ../resources/playbooks/converge.yml
+    # prepare: ../resources/playbooks/prepare.yml
+    verify: ../resources/playbooks/verify.yml
 verifier:
-  name: testinfra
-  env:
-    PYTHONWARNINGS: "ignore:.*U.*mode is deprecated:DeprecationWarning"
-  lint: |
-    set -e
-    flake8
-  options:
-    v: 1
+  name: ansible
diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml
deleted file mode 100644
index d903f567ea8ed2309c8359152b80adcf44855d1b..0000000000000000000000000000000000000000
--- a/molecule/default/prepare.yml
+++ /dev/null
@@ -1,40 +0,0 @@
----
-- name: Prepare
-  hosts: all
-  gather_facts: true
-  pre_tasks:
-    - name: include vars
-      include_vars: "../../../ansible_vaults/molecule_prepare/{{ item }}"
-      loop:
-        - "prepare.vault"
-    - name: Install python for Ansible
-      raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal)
-      become: true
-      changed_when: false
-    - name: create users (as deployed in production)
-      user:
-        name: "{{ item.name }}"
-        uid: "{{ item.uid }}"
-        create_home: "yes"
-        shell: "/bin/bash"
-      loop: "{{ vault_molecule_users | flatten(levels=1) }}"
-      become: true
-    - name: add nonfree repos
-      apt_repository:
-        repo: "deb http://ftp2.de.debian.org/debian/ buster main non-free contrib"
-        state: present
-        update-cache: "yes"
-      become: true
-    - name: Install required packages
-      apt:
-        name: [
-          'aptitude',
-          'gpg',
-          'less',
-          'libuser'
-        ]
-        state: present
-      become: true
-  roles:
-    - {role: ansible_lza_install_common, become: true}
-    - {role: ansible_lza_server_hardening, become: true}
diff --git a/molecule/default/tests/conftest.py b/molecule/default/tests/conftest.py
deleted file mode 100644
index ba0f1e81239226ea76950de710f5ec996831b7bc..0000000000000000000000000000000000000000
--- a/molecule/default/tests/conftest.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""PyTest Fixtures."""
-from __future__ import absolute_import
-import os
-import pytest
-
-
-def pytest_runtest_setup(item):
-    """Run tests only when under molecule with testinfra installed."""
-    try:
-        import testinfra
-    except ImportError:
-        pytest.skip("Test requires testinfra", allow_module_level=True)
-    if "MOLECULE_INVENTORY_FILE" in os.environ:
-        pytest.testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
-            os.environ["MOLECULE_INVENTORY_FILE"]
-        ).get_hosts("all")
-    else:
-        pytest.skip(
-            "Test should run only from inside molecule.", allow_module_level=True
-        )
diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py
deleted file mode 100644
index e9dc07fed94b0a6160d88b799ce7e52d9ddc11cf..0000000000000000000000000000000000000000
--- a/molecule/default/tests/test_default.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-def test_hosts_file(host):
-    f = host.file('/etc/hosts')
-
-    assert f.exists
-    assert f.user == 'root'
-    assert f.group == 'root'
diff --git a/molecule/default/INSTALL.rst b/molecule/resources/playbooks/INSTALL.rst
similarity index 84%
rename from molecule/default/INSTALL.rst
rename to molecule/resources/playbooks/INSTALL.rst
index 4f44b6745beb4e4a1ca19e93e42ccc8e98919d41..0c4bf5c7eb43b1b428b1824a62f8fb8a213f3600 100644
--- a/molecule/default/INSTALL.rst
+++ b/molecule/resources/playbooks/INSTALL.rst
@@ -1,6 +1,6 @@
-*******
+*********************************
 Vagrant driver installation guide
-*******
+*********************************
 
 Requirements
 ============
@@ -20,4 +20,4 @@ widely recommended `'--user' flag`_ when invoking ``pip``.
 
 .. code-block:: bash
 
-    $ pip install 'molecule[vagrant]'
+    $ pip install 'molecule_vagrant'
diff --git a/molecule/resources/playbooks/README.md b/molecule/resources/playbooks/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0c91883011699e3b0ec08bad9bcc19d201ef6331
--- /dev/null
+++ b/molecule/resources/playbooks/README.md
@@ -0,0 +1,3 @@
+This drectory contains shared playbooks and a shared Dockerfile.
+
+Visit https://molecule.readthedocs.io/en/latest/examples.html#sharing-across-scenarios for details on sharing playbooks, tests etc. across multiple scenarios.
diff --git a/molecule/resources/playbooks/converge.yml b/molecule/resources/playbooks/converge.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6817a16a4b224f11afe50d8b46afa6e7c6e777d9
--- /dev/null
+++ b/molecule/resources/playbooks/converge.yml
@@ -0,0 +1,11 @@
+---
+- name: Converge
+  hosts: all
+  pre_tasks:
+    - name: update apt cache
+      ansible.builtin.apt:
+        update_cache: true
+        upgrade: dist
+      become: true
+  roles:
+    - {name: "ansible_lza_proxy", become: true}
diff --git a/molecule/resources/playbooks/verify.yml b/molecule/resources/playbooks/verify.yml
new file mode 100644
index 0000000000000000000000000000000000000000..79044cd067d65d465136e1d68a529f32b58e0d38
--- /dev/null
+++ b/molecule/resources/playbooks/verify.yml
@@ -0,0 +1,10 @@
+---
+# This is an example playbook to execute Ansible tests.
+
+- name: Verify
+  hosts: all
+  gather_facts: false
+  tasks:
+  - name: Example assertion
+    assert:
+      that: true
diff --git a/tasks/configure-iptables.yml b/tasks/configure-iptables.yml
index d6d1e52a02860f07b96974e1f78f4cd79b1814c4..83b8706e9c57d25788c703c56002fc14e425d0b4 100644
--- a/tasks/configure-iptables.yml
+++ b/tasks/configure-iptables.yml
@@ -1,6 +1,6 @@
 ---
 - name: iptables-Regeln setzen (HTTP)
-  iptables:
+  ansible.builtin.iptables:
     action: append
     chain: INPUT
     comment: "{{ item.comment }}"
@@ -20,7 +20,7 @@
     - save iptables rules
 
 - name: iptables-Regeln setzen (HTTPS)
-  iptables:
+  ansible.builtin.iptables:
     action: append
     chain: INPUT
     comment: "{{ item.comment }}"
diff --git a/tasks/configure-nfs-mounts.yml b/tasks/configure-nfs-mounts.yml
index 1bb13713b9ee3a546c63eadec81b3799ceec1d47..650eccd8fa3a0770d42a41c18aa2d4fd7970d90f 100644
--- a/tasks/configure-nfs-mounts.yml
+++ b/tasks/configure-nfs-mounts.yml
@@ -1,7 +1,7 @@
 ---
 ### NFS-MOUNTS ###
 - name: Mountpoint fuer Logging anlegen
-  file:
+  ansible.builtin.file:
     path: "{{ vault_nfs_mounts.log.mountpoint }}"
     state: directory
     owner: "proxy"
@@ -9,7 +9,7 @@
     mode: 0755
 
 - name: NFS-Shares für Logging mounten
-  mount:
+  ansible.builtin.mount:
     name: "{{ vault_nfs_mounts.log.mountpoint }}"
     src: "{{ vault_nfs_mounts.log.share }}/{{ ansible_hostname }}"
     state: mounted
diff --git a/tasks/configure-squid-proxy.yml b/tasks/configure-squid-proxy.yml
index 04a2353927c80830ca3263befd73029262c088ad..21fb6ad43e8184738cd4aabff2fcb98cc9e5cc4b 100644
--- a/tasks/configure-squid-proxy.yml
+++ b/tasks/configure-squid-proxy.yml
@@ -1,15 +1,17 @@
 ---
 ### SQUID-PROXY KONFIGURIEREN & NEU STARTEN ###
 - name: Konfigurationsdateien einspielen - squid.conf
-  copy:
+  ansible.builtin.copy:
     src: "{{ role_path }}/../ansible_vaults/{{ role_name }}/files/etc/squid3/squid.conf.vault"
     dest: "/etc/squid/squid.conf"
+    mode: "0644"
   notify:
     - restart squid proxy
 
 - name: Konfigurationsdateien einspielen - logrotate
-  copy:
+  ansible.builtin.copy:
     src: "etc/logrotate.d/squid3"
     dest: "/etc/logrotate.d/squid3"
+    mode: "0644"
   notify:
     - restart squid proxy
diff --git a/tasks/install-packages.yml b/tasks/install-packages.yml
index 4521970dc41c9eed4b484618c638f5299ddd8aa0..7746fdd6c0088f24cf063aeab6c9b82b988067f3 100644
--- a/tasks/install-packages.yml
+++ b/tasks/install-packages.yml
@@ -1,11 +1,11 @@
 ---
 - name: install packages (rsync, sarg, squid3, tar, unzip)
-  apt:
+  ansible.builtin.apt:
     state: present
     name: [
       'rsync',
       # 'sarg',      # https://www.tecmint.com/sarg-squid-analysis-report-generator-and-internet-bandwidth-monitoring-tool/
-      'squid3',
+      'squid',
       'tar',
       'unzip'
     ]
diff --git a/tasks/main.yml b/tasks/main.yml
index f9ffb1112f11fca7084209fc67cc3d89d84d3af0..f3d7ed6df8e1e107f7fa497a510bdb03f61b18ca 100644
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -1,22 +1,22 @@
 ---
 - name: include Ansible Vaults
-  include_vars: "{{ role_path }}/../ansible_vaults/{{ role_name }}/{{ item }}"
+  ansible.builtin.include_vars: "{{ role_path }}/../ansible_vaults/{{ role_name }}/{{ item }}"
   loop:
     - "proxy.vault"
   tags: [always]
 
 - name: install Proxy packages
-  import_tasks: install-packages.yml
+  ansible.builtin.import_tasks: "install-packages.yml"
   tags: [apt]
 
 - name: configure Squid Proxy
-  import_tasks: configure-squid-proxy.yml
+  ansible.builtin.import_tasks: "configure-squid-proxy.yml"
   tags: [proxy]
 
 - name: configure NFS mounts
-  import_tasks: configure-nfs-mounts.yml
+  ansible.builtin.import_tasks: "configure-nfs-mounts.yml"
   tags: [nfs]
 
 - name: configure iptables firewall
-  import_tasks: configure-iptables.yml
+  ansible.builtin.import_tasks: "configure-iptables.yml"
   tags: [iptables]