Compare commits
	
		
			No commits in common. "main" and "role_netbird_client" have entirely different histories.
		
	
	
		
			main
			...
			role_netbi
		
	
		
@ -2,40 +2,16 @@
 | 
				
			|||||||
kubernetes:
 | 
					kubernetes:
 | 
				
			||||||
  ipPool:
 | 
					  ipPool:
 | 
				
			||||||
    ipv4:
 | 
					    ipv4:
 | 
				
			||||||
      # Minimum: /24
 | 
					 | 
				
			||||||
      cluster_cidr: 10.42.0.0/16
 | 
					      cluster_cidr: 10.42.0.0/16
 | 
				
			||||||
      service_cidr: 10.43.0.0/16
 | 
					      service_cidr: 10.43.0.0/16
 | 
				
			||||||
    ipv6:
 | 
					    ipv6:
 | 
				
			||||||
      # Minimum: /120
 | 
					 | 
				
			||||||
      cluster_cidr: fd42::/56
 | 
					      cluster_cidr: fd42::/56
 | 
				
			||||||
      service_cidr: fd43::/112
 | 
					      service_cidr: fd43::/112
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Interface to grab node-IPv4/v6 from
 | 
					    # Replace - with _
 | 
				
			||||||
    nodeIp_interface: <interface to grab nodeIp from>
 | 
					    nodeIp_interface: <interface to grab nodeIp from>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  control_plane:
 | 
					  control_plane:
 | 
				
			||||||
    dns_name: <control-plane dns-reachable-name>
 | 
					    dns_name: <control-plane dns-reachable-name>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  token: <shared token for nodes to join>
 | 
					  token: <shared token for nodes to join>
 | 
				
			||||||
 | 
					 | 
				
			||||||
  network:
 | 
					 | 
				
			||||||
    # One of [flannel, calico]
 | 
					 | 
				
			||||||
    plugin: calico
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Helper for networking
 | 
					 | 
				
			||||||
    helper:
 | 
					 | 
				
			||||||
      # https://github.com/Ruakij/RoutingTableToWg
 | 
					 | 
				
			||||||
      #  Translates received-routes from e.g. BGP to wireguard-allowedips
 | 
					 | 
				
			||||||
      #  Helpful, when nodeIp_interface is a wireguard-interface
 | 
					 | 
				
			||||||
      routingtabletowg: false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # One of [traefik-ingress]
 | 
					 | 
				
			||||||
  ingress_controller: traefik-ingress
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  config_extra:
 | 
					 | 
				
			||||||
    # etcd-tuning
 | 
					 | 
				
			||||||
    #  heartbeat: 0.5-1.5x of rtt
 | 
					 | 
				
			||||||
    #  election:  10x- of heartbeat
 | 
					 | 
				
			||||||
    etcd-arg:
 | 
					 | 
				
			||||||
      heartbeat-interval: 500
 | 
					 | 
				
			||||||
      election-timeout: 5000
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    - role: docker
 | 
					    - role: docker
 | 
				
			||||||
 | 
					    - role: netmaker
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,6 @@
 | 
				
			|||||||
  ansible.builtin.template:
 | 
					  ansible.builtin.template:
 | 
				
			||||||
    src: k3s/{{ type }}/config.yaml.jinja2
 | 
					    src: k3s/{{ type }}/config.yaml.jinja2
 | 
				
			||||||
    dest: /etc/rancher/k3s/config.yaml
 | 
					    dest: /etc/rancher/k3s/config.yaml
 | 
				
			||||||
  register: config
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: Download install-script
 | 
					- name: Download install-script
 | 
				
			||||||
  get_url:
 | 
					  get_url:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,7 @@
 | 
				
			|||||||
- name: Install K3s agent
 | 
					- name: Install K3s agent
 | 
				
			||||||
  command: /root/k3s_install.sh {{ type }} 
 | 
					  command: /root/k3s_install.sh {{ type }} 
 | 
				
			||||||
  register: command
 | 
					  register: command
 | 
				
			||||||
  changed_when: "'No change detected' not in command.stdout"
 | 
					  changed_when: "'No change detected' in command.stdout"
 | 
				
			||||||
  until: "command is not failed"
 | 
					  until: "command is not failed"
 | 
				
			||||||
  retries: 2
 | 
					  retries: 2
 | 
				
			||||||
  delay: 10
 | 
					  delay: 10
 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Make sure service is started / restarted on config change
 | 
					 | 
				
			||||||
  service:
 | 
					 | 
				
			||||||
    name: k3s-agent
 | 
					 | 
				
			||||||
    state: "{{ 'restarted' if not command.changed and config.changed else 'started' }}"
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -2,13 +2,7 @@
 | 
				
			|||||||
  command: /root/k3s_install.sh {{ type }} 
 | 
					  command: /root/k3s_install.sh {{ type }} 
 | 
				
			||||||
  when: "inventory_hostname == groups['kubernetes'][0]"
 | 
					  when: "inventory_hostname == groups['kubernetes'][0]"
 | 
				
			||||||
  register: command
 | 
					  register: command
 | 
				
			||||||
  changed_when: "'No change detected' not in command.stdout"
 | 
					  changed_when: "'No change detected' in command.stdout"
 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Make sure service is started / restarted on config change
 | 
					 | 
				
			||||||
  service:
 | 
					 | 
				
			||||||
    name: k3s
 | 
					 | 
				
			||||||
    state: "{{ 'restarted' if not command.changed and config.changed else 'started' }}"
 | 
					 | 
				
			||||||
  when: "inventory_hostname == groups['kubernetes'][0]"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: Waiting for K3s-server to accept connections
 | 
					- name: Waiting for K3s-server to accept connections
 | 
				
			||||||
  ansible.builtin.wait_for:
 | 
					  ansible.builtin.wait_for:
 | 
				
			||||||
@ -21,17 +15,11 @@
 | 
				
			|||||||
  command: /root/k3s_install.sh {{ type }}
 | 
					  command: /root/k3s_install.sh {{ type }}
 | 
				
			||||||
  when: "inventory_hostname != groups['kubernetes'][0]"
 | 
					  when: "inventory_hostname != groups['kubernetes'][0]"
 | 
				
			||||||
  register: command
 | 
					  register: command
 | 
				
			||||||
  changed_when: "'No change detected' not in command.stdout"
 | 
					  changed_when: "'No change detected' in command.stdout"
 | 
				
			||||||
  until: "command is not failed"
 | 
					  until: "command is not failed"
 | 
				
			||||||
  retries: 2
 | 
					  retries: 2
 | 
				
			||||||
  delay: 10
 | 
					  delay: 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: Make sure service is started / restarted on config change
 | 
					 | 
				
			||||||
  service:
 | 
					 | 
				
			||||||
    name: k3s
 | 
					 | 
				
			||||||
    state: "{{ 'restarted' if not command.changed and config.changed else 'started' }}"
 | 
					 | 
				
			||||||
  when: "inventory_hostname != groups['kubernetes'][0]"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Waiting for K3s-server to accept connections on other nodes
 | 
					- name: Waiting for K3s-server to accept connections on other nodes
 | 
				
			||||||
  ansible.builtin.wait_for:
 | 
					  ansible.builtin.wait_for:
 | 
				
			||||||
    host: "{{ inventory_hostname }}"
 | 
					    host: "{{ inventory_hostname }}"
 | 
				
			||||||
@ -46,10 +34,3 @@
 | 
				
			|||||||
#    block: |
 | 
					#    block: |
 | 
				
			||||||
#      export KUBECONFIG="/etc/rancher/k3s/k3s.yaml"
 | 
					#      export KUBECONFIG="/etc/rancher/k3s/k3s.yaml"
 | 
				
			||||||
#    create: true
 | 
					#    create: true
 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Deploy calico
 | 
					 | 
				
			||||||
  import_tasks: ./roles/kubernetes/tasks/install/server/network-plugin/deploy_calico.yml
 | 
					 | 
				
			||||||
  when: "kubernetes.network.plugin == 'calico'"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Deploy network-helpers
 | 
					 | 
				
			||||||
  import_tasks: ./roles/kubernetes/tasks/install/server/network-plugin/deploy_network_helper.yml
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +0,0 @@
 | 
				
			|||||||
- name: Deploy calico operator
 | 
					 | 
				
			||||||
  command: kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/tigera-operator.yaml
 | 
					 | 
				
			||||||
  register: command
 | 
					 | 
				
			||||||
  changed_when: "'created' in command.stdout"
 | 
					 | 
				
			||||||
  run_once: true
 | 
					 | 
				
			||||||
  failed_when:
 | 
					 | 
				
			||||||
  - "command.rc == 1 and 'AlreadyExists' not in command.stderr"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Deploy calico ressource template
 | 
					 | 
				
			||||||
  ansible.builtin.template:
 | 
					 | 
				
			||||||
    src: ./k3s/server/network-plugin/calico/custom-ressource.yml.jinja2
 | 
					 | 
				
			||||||
    dest: /root/calico-ressource.yml
 | 
					 | 
				
			||||||
  run_once: true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Deploy calico ressource
 | 
					 | 
				
			||||||
  command: kubectl apply -f /root/calico-ressource.yml
 | 
					 | 
				
			||||||
  register: command
 | 
					 | 
				
			||||||
  changed_when: "'created' in command.stdout"
 | 
					 | 
				
			||||||
  run_once: true
 | 
					 | 
				
			||||||
@ -1,7 +0,0 @@
 | 
				
			|||||||
- name: Deploy service-file for routing-table to wireguard-translation
 | 
					 | 
				
			||||||
  ansible.builtin.template:
 | 
					 | 
				
			||||||
    src: ./k3s/server/network-plugin/calico/routingtabletowg.yml.jinja2
 | 
					 | 
				
			||||||
    dest: /var/lib/rancher/k3s/server/manifests/routingtabletowg.yml
 | 
					 | 
				
			||||||
    mode: u=rw,g=r,o=r
 | 
					 | 
				
			||||||
  run_once: true
 | 
					 | 
				
			||||||
  when: "kubernetes.network.helper.routingtabletowg"
 | 
					 | 
				
			||||||
@ -17,26 +17,19 @@
 | 
				
			|||||||
#- name: Disable swap
 | 
					#- name: Disable swap
 | 
				
			||||||
#  command: swapoff -a
 | 
					#  command: swapoff -a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: Install required packages
 | 
					#- name: Install iptables
 | 
				
			||||||
  package:
 | 
					#  package:
 | 
				
			||||||
      name:
 | 
					#      name:
 | 
				
			||||||
          #- containerd
 | 
					#          #- containerd
 | 
				
			||||||
          #- iptables
 | 
					#          - iptables
 | 
				
			||||||
          # For Longhorn:
 | 
					#      state: latest
 | 
				
			||||||
          - nfs-common
 | 
					 | 
				
			||||||
          - open-iscsi
 | 
					 | 
				
			||||||
      state: latest
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
-  import_tasks: ./prerequisites/containerd.yml
 | 
					-  import_tasks: ./prerequisites/containerd.yml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: Gather interface-name
 | 
					 | 
				
			||||||
  set_fact:
 | 
					 | 
				
			||||||
    interface: "{{ kubernetes.ipPool.nodeIp_interface | replace('-', '_') }}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Getting nodeIp-data from interface
 | 
					- name: Getting nodeIp-data from interface
 | 
				
			||||||
  set_fact:
 | 
					  set_fact:
 | 
				
			||||||
    nodeip_ipv4: "{{ ansible_facts[ interface ].ipv4.address }}"
 | 
					    nodeip_ipv4: "{{ ansible_facts[ kubernetes.ipPool.nodeIp_interface ].ipv4.address }}"
 | 
				
			||||||
    nodeip_ipv6: "{{ ansible_facts[ interface ].ipv6[0].address if ansible_facts[ interface ].ipv6 is defined }}"
 | 
					    nodeip_ipv6: "{{ ansible_facts[ kubernetes.ipPool.nodeIp_interface ].ipv6[0].address }}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- name: Run handlers to reload configurations
 | 
					- name: Run handlers to reload configurations
 | 
				
			||||||
  meta: flush_handlers
 | 
					  meta: flush_handlers
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +1,7 @@
 | 
				
			|||||||
server: https://{{ hostvars[groups['kubernetes'][0]]['nodeip_ipv4'] }}:6443
 | 
					server: https://{{ kubernetes.control_plane.dns_name }}:6443
 | 
				
			||||||
token: '{{ kubernetes.token }}'
 | 
					token: {{ kubernetes.token }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% if nodeip_ipv6 != "" and kubernetes.ipPool.ipv6 is defined %}
 | 
					 | 
				
			||||||
node-ip: {{ nodeip_ipv4 }},{{ nodeip_ipv6 }}
 | 
					node-ip: {{ nodeip_ipv4 }},{{ nodeip_ipv6 }}
 | 
				
			||||||
{% else %}
 | 
					 | 
				
			||||||
node-ip: {{ nodeip_ipv4 }}
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Label
 | 
					# FIXME: Workaround for bug in Kubernetes 1.24/1.25 ignoring node IPv6 addresses
 | 
				
			||||||
# Region & DC
 | 
					kubelet-arg: "--node-ip=0.0.0.0"
 | 
				
			||||||
node-label:
 | 
					 | 
				
			||||||
{% if region is defined %}
 | 
					 | 
				
			||||||
  - topology.kubernetes.io/region={{ region }}
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
{% if zone is defined %}
 | 
					 | 
				
			||||||
  - topology.kubernetes.io/zone={{ zone }}
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,49 +1,23 @@
 | 
				
			|||||||
## Base ##
 | 
					## Base ##
 | 
				
			||||||
{% if inventory_hostname == groups['kubernetes'][0] %}
 | 
					{% if inventory_hostname == groups['kubernetes'][0] %}
 | 
				
			||||||
# Initialize with internal etcd
 | 
					 | 
				
			||||||
cluster-init: true
 | 
					cluster-init: true
 | 
				
			||||||
{% else %}
 | 
					{% else %}
 | 
				
			||||||
server: https://{{ hostvars[groups['kubernetes'][0]]['nodeip_ipv4'] }}:6443
 | 
					server: https://{{ groups['kubernetes'][0] }}:6443
 | 
				
			||||||
{% endif %}
 | 
					{% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
token: '{{ kubernetes.token }}'
 | 
					token: {{ kubernetes.token }}
 | 
				
			||||||
tls-san:
 | 
					tls-san:
 | 
				
			||||||
  - {{ kubernetes.control_plane.dns_name }}
 | 
					  - {{ kubernetes.control_plane.dns_name }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Networking
 | 
					# Networking
 | 
				
			||||||
{% if nodeip_ipv6 != "" and kubernetes.ipPool.ipv6 is defined %}
 | 
					 | 
				
			||||||
node-ip: {{ nodeip_ipv4 }},{{ nodeip_ipv6 }}
 | 
					node-ip: {{ nodeip_ipv4 }},{{ nodeip_ipv6 }}
 | 
				
			||||||
cluster-cidr: {{ kubernetes.ipPool.ipv4.cluster_cidr }},{{ kubernetes.ipPool.ipv6.cluster_cidr }}
 | 
					cluster-cidr: {{ kubernetes.ipPool.ipv4.cluster_cidr }},{{ kubernetes.ipPool.ipv6.cluster_cidr }}
 | 
				
			||||||
service-cidr: {{ kubernetes.ipPool.ipv4.service_cidr }},{{ kubernetes.ipPool.ipv6.service_cidr }}   
 | 
					service-cidr: {{ kubernetes.ipPool.ipv4.service_cidr }},{{ kubernetes.ipPool.ipv6.service_cidr }}   
 | 
				
			||||||
{% else %}
 | 
					 | 
				
			||||||
node-ip: {{ nodeip_ipv4 }}
 | 
					 | 
				
			||||||
cluster-cidr: {{ kubernetes.ipPool.ipv4.cluster_cidr }}
 | 
					 | 
				
			||||||
service-cidr: {{ kubernetes.ipPool.ipv4.service_cidr }}
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
egress-selector-mode: disabled
 | 
					egress-selector-mode: disabled
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Network-plugin
 | 
					# Network-plugin
 | 
				
			||||||
{% if kubernetes.network.plugin == "flannel" %}
 | 
					 | 
				
			||||||
flannel-backend: vxlan
 | 
					flannel-backend: vxlan
 | 
				
			||||||
{% else %}
 | 
					 | 
				
			||||||
disable-network-policy: true
 | 
					 | 
				
			||||||
flannel-backend: none
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Ingress-plugin
 | 
					# FIXME: Workaround for bug in Kubernetes 1.24/1.25 ignoring node IPv6 addresses
 | 
				
			||||||
{% if kubernetes.ingress_controller != "traefik-ingress" %}
 | 
					kubelet-arg: "--node-ip=0.0.0.0"
 | 
				
			||||||
disable: traefik
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Label
 | 
					 | 
				
			||||||
# Region & DC
 | 
					 | 
				
			||||||
node-label:
 | 
					 | 
				
			||||||
{% if region is defined %}
 | 
					 | 
				
			||||||
  - topology.kubernetes.io/region={{ region }}
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
{% if zone is defined %}
 | 
					 | 
				
			||||||
  - topology.kubernetes.io/zone={{ zone }}
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{{ kubernetes.config_extra | to_yaml }}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,34 +0,0 @@
 | 
				
			|||||||
# This section includes base Calico installation configuration.
 | 
					 | 
				
			||||||
# For more information, see: https://projectcalico.docs.tigera.io/master/reference/installation/api#operator.tigera.io/v1.Installation
 | 
					 | 
				
			||||||
apiVersion: operator.tigera.io/v1
 | 
					 | 
				
			||||||
kind: Installation
 | 
					 | 
				
			||||||
metadata:
 | 
					 | 
				
			||||||
  name: default
 | 
					 | 
				
			||||||
spec:
 | 
					 | 
				
			||||||
  # Configures Calico networking.
 | 
					 | 
				
			||||||
  calicoNetwork:
 | 
					 | 
				
			||||||
    # Note: The ipPools section cannot be modified post-install.
 | 
					 | 
				
			||||||
    ipPools:
 | 
					 | 
				
			||||||
    - blockSize: 26
 | 
					 | 
				
			||||||
      cidr: {{ kubernetes.ipPool.ipv4.cluster_cidr }}
 | 
					 | 
				
			||||||
      encapsulation: None
 | 
					 | 
				
			||||||
      natOutgoing: Enabled
 | 
					 | 
				
			||||||
      nodeSelector: all()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% if nodeip_ipv6 != "" and kubernetes.ipPool.ipv6 is defined %}
 | 
					 | 
				
			||||||
    - blockSize: 122
 | 
					 | 
				
			||||||
      cidr: {{ kubernetes.ipPool.ipv6.cluster_cidr }}
 | 
					 | 
				
			||||||
      encapsulation: None
 | 
					 | 
				
			||||||
      natOutgoing: Enabled
 | 
					 | 
				
			||||||
      nodeSelector: all()
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This section configures the Calico API server.
 | 
					 | 
				
			||||||
# For more information, see: https://projectcalico.docs.tigera.io/master/reference/installation/api#operator.tigera.io/v1.APIServer
 | 
					 | 
				
			||||||
apiVersion: operator.tigera.io/v1
 | 
					 | 
				
			||||||
kind: APIServer 
 | 
					 | 
				
			||||||
metadata: 
 | 
					 | 
				
			||||||
  name: default 
 | 
					 | 
				
			||||||
spec: {}
 | 
					 | 
				
			||||||
@ -1,45 +0,0 @@
 | 
				
			|||||||
# https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/
 | 
					 | 
				
			||||||
apiVersion: apps/v1
 | 
					 | 
				
			||||||
kind: DaemonSet
 | 
					 | 
				
			||||||
metadata:
 | 
					 | 
				
			||||||
  name: routingtabletowg
 | 
					 | 
				
			||||||
  namespace: calico-system
 | 
					 | 
				
			||||||
  labels:
 | 
					 | 
				
			||||||
    app: routingtabletowg
 | 
					 | 
				
			||||||
spec:
 | 
					 | 
				
			||||||
  selector:
 | 
					 | 
				
			||||||
    matchLabels:
 | 
					 | 
				
			||||||
      app: routingtabletowg
 | 
					 | 
				
			||||||
  template:
 | 
					 | 
				
			||||||
    metadata:
 | 
					 | 
				
			||||||
      labels:
 | 
					 | 
				
			||||||
        app: routingtabletowg
 | 
					 | 
				
			||||||
    spec:
 | 
					 | 
				
			||||||
      tolerations:
 | 
					 | 
				
			||||||
      # this toleration is to have the daemonset runnable on master nodes
 | 
					 | 
				
			||||||
      # remove it if your masters can't run pods
 | 
					 | 
				
			||||||
      - key: node-role.kubernetes.io/master
 | 
					 | 
				
			||||||
        effect: NoSchedule
 | 
					 | 
				
			||||||
      hostNetwork: true
 | 
					 | 
				
			||||||
      containers:
 | 
					 | 
				
			||||||
        - name: routingtabletowg
 | 
					 | 
				
			||||||
          image: "ruakij/routingtabletowg:0.2.0"
 | 
					 | 
				
			||||||
          env:
 | 
					 | 
				
			||||||
            - name: INTERFACE
 | 
					 | 
				
			||||||
              value: {{ kubernetes.ipPool.nodeIp_interface }}
 | 
					 | 
				
			||||||
            - name: FILTER_PROTOCOL
 | 
					 | 
				
			||||||
              value: bird
 | 
					 | 
				
			||||||
            - name: PERIODIC_SYNC
 | 
					 | 
				
			||||||
              value: '300'
 | 
					 | 
				
			||||||
          securityContext:
 | 
					 | 
				
			||||||
            capabilities:
 | 
					 | 
				
			||||||
              add:
 | 
					 | 
				
			||||||
                - NET_ADMIN
 | 
					 | 
				
			||||||
          resources:
 | 
					 | 
				
			||||||
            requests:
 | 
					 | 
				
			||||||
              cpu: 10m
 | 
					 | 
				
			||||||
              memory: 10Mi
 | 
					 | 
				
			||||||
            limits:
 | 
					 | 
				
			||||||
              cpu: 20m
 | 
					 | 
				
			||||||
              memory: 20Mi
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
@ -1,29 +0,0 @@
 | 
				
			|||||||
nftables:
 | 
					 | 
				
			||||||
  # Rules to add
 | 
					 | 
				
			||||||
  #  Handled as templates
 | 
					 | 
				
			||||||
  #  Creates separate files for each entry.
 | 
					 | 
				
			||||||
  # The identifier is necessary for ansible to be able to merge the keys (when 'hash_behaviour = merge')
 | 
					 | 
				
			||||||
  #  rule-ids have to be unique across files and raw
 | 
					 | 
				
			||||||
  rules:
 | 
					 | 
				
			||||||
    # Files with Rules to add
 | 
					 | 
				
			||||||
    files:
 | 
					 | 
				
			||||||
      #'<group_identifier>': '<relative-location>'
 | 
					 | 
				
			||||||
      #'<group_identifier>':
 | 
					 | 
				
			||||||
      #  main: <relative-location>
 | 
					 | 
				
			||||||
      #  '<identifier>': '<relative-location>'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Rules to add
 | 
					 | 
				
			||||||
    raw:
 | 
					 | 
				
			||||||
      #'<group_identifier>': '<content>'
 | 
					 | 
				
			||||||
      #'<group_identifier>':
 | 
					 | 
				
			||||||
      #  main: <content>
 | 
					 | 
				
			||||||
      #  '<identifier>': '<content>'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # Decides if /etc/nftables.conf is applied or separate files which have changed
 | 
					 | 
				
			||||||
  #  Separate changes require the files to be self-tyding to not end up with duplicate rules
 | 
					 | 
				
			||||||
  #  e.g.
 | 
					 | 
				
			||||||
  #   table ip mytable
 | 
					 | 
				
			||||||
  #   flush table ip mytable
 | 
					 | 
				
			||||||
  #   delete table ip mytable
 | 
					 | 
				
			||||||
  #   table ip mytable {} ...
 | 
					 | 
				
			||||||
  apply_global: false
 | 
					 | 
				
			||||||
@ -1,8 +0,0 @@
 | 
				
			|||||||
- name: Load group rules
 | 
					 | 
				
			||||||
  command: "nft -f /etc/nftables/ansible-managed/{{ item }}.nft"
 | 
					 | 
				
			||||||
  loop: "{{ combined_rules | list }}"
 | 
					 | 
				
			||||||
  when: not nftables.apply_global
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Load global rule file
 | 
					 | 
				
			||||||
  command: "nft -f /etc/nftables.nft"
 | 
					 | 
				
			||||||
  when: nftables.apply_global
 | 
					 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
- name: Deploying group files
 | 
					 | 
				
			||||||
  include_tasks: ./per-group-template-file.yml
 | 
					 | 
				
			||||||
  with_items:
 | 
					 | 
				
			||||||
    - "{{ nftables.rules.files | list }}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Deploying group raw-files
 | 
					 | 
				
			||||||
  include_tasks: ./per-group-template.yml
 | 
					 | 
				
			||||||
  with_items:
 | 
					 | 
				
			||||||
    - "{{ nftables.rules.raw | list }}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- include_tasks: ./remove-files.yml
 | 
					 | 
				
			||||||
@ -1,51 +0,0 @@
 | 
				
			|||||||
- set_fact:
 | 
					 | 
				
			||||||
    group_identifier: "{{ item }}"
 | 
					 | 
				
			||||||
    value: "{{ nftables.rules.files[item] }}"
 | 
					 | 
				
			||||||
  when: "item is defined"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  #'<group_identifier>': '<relative-location>'
 | 
					 | 
				
			||||||
- block:
 | 
					 | 
				
			||||||
  - name: Create main rule file
 | 
					 | 
				
			||||||
    template:
 | 
					 | 
				
			||||||
      src: "{{ value }}"
 | 
					 | 
				
			||||||
      dest: "/etc/nftables/ansible-managed/{{ group_identifier }}.nft"
 | 
					 | 
				
			||||||
  when: value is string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  #'<group_identifier>':
 | 
					 | 
				
			||||||
  #  main: <relative-location>
 | 
					 | 
				
			||||||
  #  '<identifier>': '<relative-location>'
 | 
					 | 
				
			||||||
- block:
 | 
					 | 
				
			||||||
  - set_fact:
 | 
					 | 
				
			||||||
      items: "{{ nftables.rules.files[item] }}"
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
  - block:
 | 
					 | 
				
			||||||
    - name: Create main rule file
 | 
					 | 
				
			||||||
      template:
 | 
					 | 
				
			||||||
        src: "{{ items['main'] }}"
 | 
					 | 
				
			||||||
        dest: "/etc/nftables/ansible-managed/{{ group_identifier }}.nft"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    - name: Include rule files
 | 
					 | 
				
			||||||
      lineinfile:
 | 
					 | 
				
			||||||
        path: "/etc/nftables/ansible-managed/{{ group_identifier }}.nft"
 | 
					 | 
				
			||||||
        regexp: "include\\s+(\"|')\\/etc\\/nftables\\/ansible-managed\\/{{ group_identifier }}\\/.*$"
 | 
					 | 
				
			||||||
        line: 'include "/etc/nftables/ansible-managed/{{ group_identifier }}/*.nft"'
 | 
					 | 
				
			||||||
    when: items['main'] is defined
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: Create group folder
 | 
					 | 
				
			||||||
    file:
 | 
					 | 
				
			||||||
      path: "/etc/nftables/ansible-managed/{{ group_identifier }}/"
 | 
					 | 
				
			||||||
      state: directory
 | 
					 | 
				
			||||||
    when: items|length > 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - set_fact:
 | 
					 | 
				
			||||||
      test: "{{ items | dict2items | selectattr('key', 'ne', 'main') }}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: Create included rule files
 | 
					 | 
				
			||||||
    template:
 | 
					 | 
				
			||||||
      src: "{{ fileItem.value }}"
 | 
					 | 
				
			||||||
      dest: "/etc/nftables/ansible-managed/{{ group_identifier }}/{{ fileItem.key }}.nft"
 | 
					 | 
				
			||||||
    loop: "{{ items | dict2items | selectattr('key', 'ne', 'main') }}"
 | 
					 | 
				
			||||||
    loop_control:
 | 
					 | 
				
			||||||
      loop_var: fileItem
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  when: value is mapping
 | 
					 | 
				
			||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
- set_fact:
 | 
					 | 
				
			||||||
    group_identifier: "{{ item }}"
 | 
					 | 
				
			||||||
    value: "{{ nftables.rules.raw[item] }}"
 | 
					 | 
				
			||||||
  when: "item is defined"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  #'<group_identifier>': '<content>'
 | 
					 | 
				
			||||||
- block:
 | 
					 | 
				
			||||||
  - name: Create main rule file
 | 
					 | 
				
			||||||
    copy:
 | 
					 | 
				
			||||||
      content: "{{ value }}"
 | 
					 | 
				
			||||||
      dest: "/etc/nftables/ansible-managed/{{ group_identifier }}.nft"
 | 
					 | 
				
			||||||
  when: value is string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  #'<group_identifier>':
 | 
					 | 
				
			||||||
  #  main: <content>
 | 
					 | 
				
			||||||
  #  '<identifier>': '<content>'
 | 
					 | 
				
			||||||
- block:
 | 
					 | 
				
			||||||
  - set_fact:
 | 
					 | 
				
			||||||
      items: "{{ nftables.rules.raw[item] }}"
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  - block:
 | 
					 | 
				
			||||||
    - name: Create main rule file
 | 
					 | 
				
			||||||
      copy:
 | 
					 | 
				
			||||||
        content: "{{ items['main'] }}"
 | 
					 | 
				
			||||||
        dest: "/etc/nftables/ansible-managed/{{ group_identifier }}.nft"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    - name: Include rule files
 | 
					 | 
				
			||||||
      lineinfile:
 | 
					 | 
				
			||||||
        path: "/etc/nftables/ansible-managed/{{ group_identifier }}.nft"
 | 
					 | 
				
			||||||
        regexp: "include\\s+(\"|')\\/etc\\/nftables\\/ansible-managed\\/{{ group_identifier }}\\/.*$"
 | 
					 | 
				
			||||||
        line: 'include "/etc/nftables/ansible-managed/{{ group_identifier }}/*.nft"'
 | 
					 | 
				
			||||||
    when: items['main'] is defined
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: Create group folder
 | 
					 | 
				
			||||||
    file:
 | 
					 | 
				
			||||||
      path: "/etc/nftables/ansible-managed/{{ group_identifier }}/"
 | 
					 | 
				
			||||||
      state: directory
 | 
					 | 
				
			||||||
    when: items|length > 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: Create included rule files
 | 
					 | 
				
			||||||
    copy:
 | 
					 | 
				
			||||||
      content: "{{ included_item.value }}"
 | 
					 | 
				
			||||||
      dest: "/etc/nftables/ansible-managed/{{ group_identifier }}/{{ included_item.key }}.nft"
 | 
					 | 
				
			||||||
    loop: "{{ items | dict2items | selectattr('key', 'ne', 'main') }}"
 | 
					 | 
				
			||||||
    loop_control:
 | 
					 | 
				
			||||||
      loop_var: included_item
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  when: value is mapping
 | 
					 | 
				
			||||||
@ -1,4 +0,0 @@
 | 
				
			|||||||
- name: Install Packages
 | 
					 | 
				
			||||||
  package:
 | 
					 | 
				
			||||||
    name:
 | 
					 | 
				
			||||||
      - nftables
 | 
					 | 
				
			||||||
@ -1,7 +0,0 @@
 | 
				
			|||||||
- import_tasks: ./prerequisites.yml
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- import_tasks: ./setup-packages.yml
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- import_tasks: ./deploy-rules/main.yml
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- import_tasks: ./apply-files.yml
 | 
					 | 
				
			||||||
@ -1,13 +0,0 @@
 | 
				
			|||||||
# Defaults if missing
 | 
					 | 
				
			||||||
- name: Set defaults if missing
 | 
					 | 
				
			||||||
  set_fact:
 | 
					 | 
				
			||||||
    nftables:
 | 
					 | 
				
			||||||
      rules:
 | 
					 | 
				
			||||||
        files: "{{ nftables.rules.files | default({}) | combine({}) }}"
 | 
					 | 
				
			||||||
        raw: "{{ nftables.rules.raw | default({}) | combine({}) }}"
 | 
					 | 
				
			||||||
    combined_rules: "{{ nftables.rules.raw | combine(nftables.rules.files, recursive=true) }}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#- name: Check items for consistency
 | 
					 | 
				
			||||||
#  assert:
 | 
					 | 
				
			||||||
#    that: "{{ nftables.rules.files.values() | length }} + {{ nftables.rules.raw.values() | length }} == {{ combined_rules.values() | length }}"
 | 
					 | 
				
			||||||
#    fail_msg: "files and raw rules share the same identifier"
 | 
					 | 
				
			||||||
@ -1,21 +0,0 @@
 | 
				
			|||||||
- name: Handle removed group files
 | 
					 | 
				
			||||||
  block:
 | 
					 | 
				
			||||||
  - find:
 | 
					 | 
				
			||||||
      paths: /etc/nftables/ansible-managed/
 | 
					 | 
				
			||||||
      file_type: 'any'
 | 
					 | 
				
			||||||
      excludes: '{% for item in combined_rules %}{{ item }},{{ item }}.nft,{% endfor %}'
 | 
					 | 
				
			||||||
      depth: 1
 | 
					 | 
				
			||||||
    register: removeFiles
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - file:
 | 
					 | 
				
			||||||
      path: "{{ fileItem.path }}"
 | 
					 | 
				
			||||||
      state: absent
 | 
					 | 
				
			||||||
    loop: "{{ removeFiles.files }}"
 | 
					 | 
				
			||||||
    loop_control:
 | 
					 | 
				
			||||||
      label: "{{ fileItem.path }}"
 | 
					 | 
				
			||||||
      loop_var: fileItem
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Handle removed included files per group
 | 
					 | 
				
			||||||
  include_tasks: ./remove-per-group.yml
 | 
					 | 
				
			||||||
  with_items:
 | 
					 | 
				
			||||||
  - "{{ combined_rules | list }}"
 | 
					 | 
				
			||||||
@ -1,20 +0,0 @@
 | 
				
			|||||||
- set_fact:
 | 
					 | 
				
			||||||
    group_identifier: "{{ item }}"
 | 
					 | 
				
			||||||
    group_items: "{{ combined_rules[item] }}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- block:
 | 
					 | 
				
			||||||
  - find:
 | 
					 | 
				
			||||||
      paths: "/etc/nftables/ansible-managed/{{ group_identifier }}/"
 | 
					 | 
				
			||||||
      file_type: 'any'
 | 
					 | 
				
			||||||
      excludes: '{% for item in group_items %}{{ item }}.nft,{% endfor %}'
 | 
					 | 
				
			||||||
    register: removeFiles
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - file:
 | 
					 | 
				
			||||||
      path: "{{ fileItem.path }}"
 | 
					 | 
				
			||||||
      state: absent
 | 
					 | 
				
			||||||
    loop: "{{ removeFiles.files }}"
 | 
					 | 
				
			||||||
    loop_control:
 | 
					 | 
				
			||||||
      label: "{{ fileItem.path }}"
 | 
					 | 
				
			||||||
      loop_var: fileItem
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  when: group_items is mapping
 | 
					 | 
				
			||||||
@ -1,15 +0,0 @@
 | 
				
			|||||||
- name: Install nftables
 | 
					 | 
				
			||||||
  package:
 | 
					 | 
				
			||||||
    name:
 | 
					 | 
				
			||||||
      - nftables
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Create /etc/nftables/ansible-managed
 | 
					 | 
				
			||||||
  file:
 | 
					 | 
				
			||||||
    path: /etc/nftables/ansible-managed
 | 
					 | 
				
			||||||
    state: directory
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Include files in /etc/nftables/ansible-managed/ from /etc/nftables.conf
 | 
					 | 
				
			||||||
  blockinfile:
 | 
					 | 
				
			||||||
    path: /etc/nftables.conf
 | 
					 | 
				
			||||||
    marker: "# {mark} ANSIBLE MANAGED BLOCK - nftables"
 | 
					 | 
				
			||||||
    content: 'include "/etc/nftables/ansible-managed/*.nft"'
 | 
					 | 
				
			||||||
@ -1,12 +0,0 @@
 | 
				
			|||||||
wireguard_ipv6_converter:
 | 
					 | 
				
			||||||
  version: latest
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # see https://github.com/Ruakij/wg-ipv6-converter#31-environment
 | 
					 | 
				
			||||||
  setup:
 | 
					 | 
				
			||||||
    interface: wg0
 | 
					 | 
				
			||||||
    #ipv6_format: fc12::%02x%02x:%02x%02x/%d
 | 
					 | 
				
			||||||
    #filter_prefix: 100.100
 | 
					 | 
				
			||||||
    #recheck_interval: 60s
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  service:
 | 
					 | 
				
			||||||
    #bindTo: netbird.service
 | 
					 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
- name: Get architecture
 | 
					 | 
				
			||||||
  set_fact:
 | 
					 | 
				
			||||||
    arch: "{{ 'amd64' if ansible_architecture == 'x86_64' else 'arm64' }}"
 | 
					 | 
				
			||||||
    versionUri: "{% if wireguard_ipv6_converter.version == 'latest' %}latest/download{% else %}download/{{ wireguard_ipv6_converter.version }}{% endif %}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Download binary
 | 
					 | 
				
			||||||
  get_url:
 | 
					 | 
				
			||||||
    url: https://github.com/Ruakij/wg-ipv6-converter/releases/{{ versionUri }}/wg-ipv6-converter_{{ arch }}
 | 
					 | 
				
			||||||
    dest: /usr/local/bin/wg-ipv6-converter
 | 
					 | 
				
			||||||
    mode: "744"
 | 
					 | 
				
			||||||
  register: deployDownload
 | 
					 | 
				
			||||||
@ -1,3 +0,0 @@
 | 
				
			|||||||
- import_tasks: ./deploy.yml
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- import_tasks: ./setup-service.yml
 | 
					 | 
				
			||||||
@ -1,27 +0,0 @@
 | 
				
			|||||||
- name: Deploy service
 | 
					 | 
				
			||||||
  ansible.builtin.template:
 | 
					 | 
				
			||||||
    src: wg-ipv6-conv.service.jinja2
 | 
					 | 
				
			||||||
    dest: /etc/systemd/system/wg-ipv6-converter_{{ wireguard_ipv6_converter.setup.interface }}.service
 | 
					 | 
				
			||||||
  register: serviceFile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Enable service
 | 
					 | 
				
			||||||
  ansible.builtin.service:
 | 
					 | 
				
			||||||
    name: wg-ipv6-converter_{{ wireguard_ipv6_converter.setup.interface }}
 | 
					 | 
				
			||||||
    daemon-reload: true
 | 
					 | 
				
			||||||
    enabled: true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Start service if interface exists already
 | 
					 | 
				
			||||||
  ansible.builtin.service:
 | 
					 | 
				
			||||||
    name: wg-ipv6-converter_{{ wireguard_ipv6_converter.setup.interface }}
 | 
					 | 
				
			||||||
    state: "{{ 'restarted' if deployDownload.changed or serviceFile.changed else 'started' }}"
 | 
					 | 
				
			||||||
  register: service
 | 
					 | 
				
			||||||
  when: "wireguard_ipv6_converter.setup.interface in ansible_interfaces"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Pause for 5s to wait for program to have run
 | 
					 | 
				
			||||||
  ansible.builtin.pause:
 | 
					 | 
				
			||||||
    seconds: 5
 | 
					 | 
				
			||||||
  when: "service.changed"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- name: Gather facts to get changes
 | 
					 | 
				
			||||||
  ansible.builtin.gather_facts:
 | 
					 | 
				
			||||||
  when: "service.changed"
 | 
					 | 
				
			||||||
@ -1,29 +0,0 @@
 | 
				
			|||||||
[Unit]
 | 
					 | 
				
			||||||
Description=WireGuard IPv6 converter for {{ wireguard_ipv6_converter.setup.interface }}
 | 
					 | 
				
			||||||
{% if wireguard_ipv6_converter.service.bindTo is defined %}
 | 
					 | 
				
			||||||
BindsTo={{ wireguard_ipv6_converter.service.bindTo }}
 | 
					 | 
				
			||||||
After={{ wireguard_ipv6_converter.service.bindTo }}
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[Service]
 | 
					 | 
				
			||||||
Type=simple
 | 
					 | 
				
			||||||
{% if wireguard_ipv6_converter.service.bindTo is defined %}
 | 
					 | 
				
			||||||
ExecStartPre=/bin/sleep 10
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
ExecStart=/usr/local/bin/wg-ipv6-converter
 | 
					 | 
				
			||||||
Restart=always
 | 
					 | 
				
			||||||
RestartSec=30
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Environment="INTERFACE={{ wireguard_ipv6_converter.setup.interface }}"
 | 
					 | 
				
			||||||
{% if wireguard_ipv6_converter.setup.ipv6_format is defined %}
 | 
					 | 
				
			||||||
Environment="IPV6_FORMAT={{ wireguard_ipv6_converter.setup.ipv6_format }}"
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
{% if wireguard_ipv6_converter.setup.filter_prefix is defined %}
 | 
					 | 
				
			||||||
Environment="FILTER_PREFIX={{ wireguard_ipv6_converter.setup.filter_prefix }}"
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
{% if wireguard_ipv6_converter.setup.recheck_interval is defined %}
 | 
					 | 
				
			||||||
Environment="RECHECK_INTERVAL={{ wireguard_ipv6_converter.setup.recheck_interval }}"
 | 
					 | 
				
			||||||
{% endif %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[Install]
 | 
					 | 
				
			||||||
WantedBy=multi-user.target
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user