feat/renew-certificates #26
@ -2,7 +2,7 @@
|
||||
# defaults
|
||||
hashistack_ca_directory: "{{ hashistack_sub_configuration_directories['certificates'] }}"
|
||||
hashistack_ca_use_cryptography: false
|
||||
hashistack_ca_action: "noop"
|
||||
# hashistack_ca_action: "noop"
|
||||
hashistack_ca_domain: example.com
|
||||
hashistack_ca_directory_owner: "{{ lookup('env', 'USER') }}"
|
||||
|
||||
@ -79,37 +79,52 @@ hashistack_ca_leaf_renew_threshold: 30d
|
||||
############################
|
||||
hashistack_ca_consul_org_name: "{{ hashistack_ca_root_org_name }}"
|
||||
hashistack_ca_consul_common_name: "{{ inventory_hostname }}"
|
||||
hashistack_ca_consul_csr_sans:
|
||||
- "DNS:{{ inventory_hostname }}"
|
||||
- "DNS:consul.service.consul"
|
||||
- "DNS:localhost"
|
||||
- "IP:{{ api_interface_address }}"
|
||||
- "IP:127.0.0.1"
|
||||
- "{{ 'DNS:server.' ~ consul_datacenter ~ '.' ~ consul_domain if consul_enable_server else omit }}"
|
||||
hashistack_ca_consul_csr_sans: >
|
||||
{{
|
||||
[
|
||||
"DNS:" + inventory_hostname,
|
||||
"DNS:consul.service.consul",
|
||||
"DNS:localhost",
|
||||
"IP:" + api_interface_address,
|
||||
"IP:127.0.0.1"
|
||||
]
|
||||
+ ([ "DNS:server." + consul_datacenter + "." + consul_domain ] if consul_enable_server else [])
|
||||
}}
|
||||
|
||||
###########################
|
||||
# Nomad Leaf Certificates #
|
||||
###########################
|
||||
hashistack_ca_nomad_org_name: "{{ hashistack_ca_root_org_name }}"
|
||||
hashistack_ca_nomad_common_name: "{{ inventory_hostname }}"
|
||||
hashistack_ca_nomad_csr_sans:
|
||||
- "DNS:{{ inventory_hostname }}"
|
||||
- "DNS:localhost"
|
||||
- "IP:{{ api_interface_address }}"
|
||||
- "IP:127.0.0.1"
|
||||
- "{{ 'DNS:server.' ~ nomad_region ~ '.nomad' if nomad_enable_server else omit }}"
|
||||
- "{{ 'DNS:nomad.service.consul' if (nomad_enable_server and enable_consul) else omit }}"
|
||||
hashistack_ca_nomad_csr_sans: >
|
||||
{{
|
||||
[
|
||||
"DNS:" + inventory_hostname,
|
||||
"DNS:localhost",
|
||||
"IP:" + api_interface_address,
|
||||
"IP:127.0.0.1"
|
||||
]
|
||||
+ ([ "DNS:server." + nomad_region + ".nomad" ] if nomad_enable_server else [])
|
||||
+ ([ "DNS:client." + nomad_region + ".nomad" ] if nomad_enable_client else [])
|
||||
+ ([ "DNS:nomad.service.consul" ] if (nomad_enable_server and enable_consul) else [])
|
||||
}}
|
||||
|
||||
###########################
|
||||
# Vault Leaf Certificates #
|
||||
###########################
|
||||
hashistack_ca_vault_org_name: "{{ hashistack_ca_root_org_name }}"
|
||||
hashistack_ca_vault_common_name: "{{ inventory_hostname }}"
|
||||
hashistack_ca_vault_csr_sans:
|
||||
- "DNS:{{ inventory_hostname }}"
|
||||
- "{{ 'DNS:active.vault.service.consul' if enable_consul else omit }}"
|
||||
- "{{ 'DNS:standby.vault.service.consul' if enable_consul else omit }}"
|
||||
- "{{ 'DNS:vault.service.consul' if enable_consul else omit }}"
|
||||
- "DNS:localhost"
|
||||
- "IP:{{ api_interface_address }}"
|
||||
- "IP:127.0.0.1"
|
||||
hashistack_ca_vault_csr_sans: >
|
||||
{{
|
||||
[
|
||||
"DNS:" + inventory_hostname,
|
||||
"DNS:localhost",
|
||||
"IP:" + api_interface_address,
|
||||
"IP:127.0.0.1"
|
||||
]
|
||||
+ ([
|
||||
"DNS:active.vault.service.consul",
|
||||
"DNS:standby.vault.service.consul",
|
||||
"DNS:vault.service.consul"
|
||||
] if enable_consul else [])
|
||||
}}
|
||||
|
@ -24,6 +24,31 @@
|
||||
when: _consul_env_file.changed or
|
||||
_consul_config_file.changed
|
||||
|
||||
- name: "Consul | Gather initial checksums for certificate files"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item }}"
|
||||
checksum_algorithm: sha1
|
||||
loop: "{{ consul_certificates_reload_watchlist }}"
|
||||
when: consul_enable_tls
|
||||
register: _consul_initial_cert_checksums
|
||||
|
||||
- name: "Consul | Normalize initial checksums"
|
||||
ansible.builtin.set_fact:
|
||||
# This needs to be optimized, but I have spent so much time on it not
|
||||
# working that I will keep it as is for now, and we'll see later.
|
||||
_consul_initial_checksums_normalized: >-
|
||||
{% filter trim %}
|
||||
{% set checksums = [] %}
|
||||
{% for item in _consul_initial_cert_checksums.results %}
|
||||
{% set _ = checksums.append({
|
||||
'item': item.item,
|
||||
'initial_checksum': (item.stat.checksum | default('absent'))
|
||||
}) %}
|
||||
{% endfor %}
|
||||
{{ checksums }}
|
||||
{% endfilter %}
|
||||
when: consul_enable_tls
|
||||
|
||||
- name: "Consul | Copy extra configuration files"
|
||||
when: consul_extra_files
|
||||
block:
|
||||
@ -72,3 +97,44 @@
|
||||
loop_control:
|
||||
loop_var: dir_source_item
|
||||
when: _consul_dir_sources is defined
|
||||
|
||||
- name: "Consul | Gather final checksums for certificate files"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item }}"
|
||||
checksum_algorithm: sha1
|
||||
loop: "{{ consul_certificates_reload_watchlist }}"
|
||||
when: consul_enable_tls
|
||||
register: _consul_final_cert_checksums
|
||||
|
||||
- name: "Consul | Normalize final checksums"
|
||||
ansible.builtin.set_fact:
|
||||
# This needs to be optimized, but I have spent so much time on it not
|
||||
# working that I will keep it as is for now, and we'll see later.
|
||||
_consul_final_checksums_normalized: >-
|
||||
{% filter trim %}
|
||||
{% set checksums = [] %}
|
||||
{% for item in _consul_final_cert_checksums.results %}
|
||||
{% set _ = checksums.append({
|
||||
'item': item.item,
|
||||
'final_checksum': (item.stat.checksum | default('absent'))
|
||||
}) %}
|
||||
{% endfor %}
|
||||
{{ checksums }}
|
||||
{% endfilter %}
|
||||
when: consul_enable_tls
|
||||
|
||||
- name: "Consul | Merge initial and final checksum lists"
|
||||
ansible.builtin.set_fact:
|
||||
_consul_checksums_list: >-
|
||||
{{
|
||||
_consul_initial_checksums_normalized |
|
||||
community.general.lists_mergeby(_consul_final_checksums_normalized, 'item')
|
||||
}}
|
||||
when: consul_enable_tls
|
||||
|
||||
- name: "Consul | Determine if certificates have changed or were newly added"
|
||||
ansible.builtin.set_fact:
|
||||
_consul_service_need_reload: true
|
||||
when:
|
||||
- consul_enable_tls
|
||||
- _consul_checksums_list | json_query('[?initial_checksum!=final_checksum]') | list| length > 0
|
||||
|
@ -136,6 +136,6 @@
|
||||
|
||||
- name: "Consul | Set reload-check & restart-check variable"
|
||||
ansible.builtin.set_fact:
|
||||
_consul_service_need_reload: true
|
||||
_consul_service_need_daemon_reload: true
|
||||
_consul_service_need_restart: true
|
||||
when: _consul_unit_file.changed # noqa: no-handler
|
||||
|
@ -2,6 +2,7 @@
|
||||
# task/main file for consul
|
||||
- name: "Consul | Set reload-check & restart-check variable"
|
||||
ansible.builtin.set_fact:
|
||||
_consul_service_need_daemon_reload: false
|
||||
_consul_service_need_reload: false
|
||||
_consul_service_need_restart: false
|
||||
|
||||
@ -37,11 +38,23 @@
|
||||
- name: "Consul | Reload systemd daemon"
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
when: _consul_service_need_reload
|
||||
when: _consul_service_need_daemon_reload
|
||||
|
||||
- name: "Consul | Start service: {{ consul_service_name }}"
|
||||
ansible.builtin.service:
|
||||
name: "{{ consul_service_name }}"
|
||||
state: restarted
|
||||
throttle: 1
|
||||
when: _consul_service_need_restart
|
||||
when:
|
||||
- consul_start_service
|
||||
- _consul_service_need_restart
|
||||
|
||||
- name: "Consul | Reload service: {{ consul_service_name }}"
|
||||
ansible.builtin.service:
|
||||
name: "{{ consul_service_name }}"
|
||||
state: reloaded
|
||||
throttle: 1
|
||||
when:
|
||||
- consul_start_service
|
||||
- _consul_service_need_reload
|
||||
- not _consul_service_need_restart
|
||||
|
@ -21,6 +21,19 @@ consul_github_project: hashicorp/consul
|
||||
consul_github_url: https://github.com
|
||||
consul_repository_url: https://releases.hashicorp.com/consul
|
||||
|
||||
consul_certificates_reload_watchlist: |
|
||||
{% filter trim %}
|
||||
{% set watchlist = [] %}
|
||||
{% for block, config in (consul_configuration.tls | default({})).items() %}
|
||||
{% for key in ['ca_file', 'cert_file', 'key_file'] %}
|
||||
{% if config.get(key) %}
|
||||
{{ watchlist.append(config[key]) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{{ watchlist | unique }}
|
||||
{% endfilter %}
|
||||
|
||||
consul_configuration:
|
||||
domain: "{{ consul_domain }}"
|
||||
datacenter: "{{ consul_datacenter }}"
|
||||
|
@ -45,3 +45,15 @@
|
||||
when:
|
||||
- hashistack_ca_renew_leaf
|
||||
- "('consul_servers' in group_names) or ('consul_agents' in group_names)"
|
||||
|
||||
- name: "Nomad leaf certificates | Import renew_nomad.yml"
|
||||
ansible.builtin.include_tasks: renew/renew_nomad.yml
|
||||
when:
|
||||
- hashistack_ca_renew_leaf
|
||||
- "('nomad_servers' in group_names) or ('nomad_clients' in group_names)"
|
||||
|
||||
- name: "Vault leaf certificates | Import renew_vault.yml"
|
||||
ansible.builtin.include_tasks: renew/renew_vault.yml
|
||||
when:
|
||||
- hashistack_ca_renew_leaf
|
||||
- "'vault_servers' in group_names"
|
||||
|
@ -2,7 +2,7 @@
|
||||
# meta file for nomad
|
||||
galaxy_info:
|
||||
namespace: "ednz_cloud"
|
||||
role_name: "hashicorp_nomad"
|
||||
role_name: "nomad"
|
||||
author: "Bertrand Lanson"
|
||||
description: "Install and configure hashicorp nomad for debian-based distros."
|
||||
license: "license (BSD, MIT)"
|
||||
|
@ -24,6 +24,31 @@
|
||||
when: _nomad_env_file.changed or
|
||||
_nomad_config_file.changed
|
||||
|
||||
- name: "Nomad | Gather initial checksums for certificate files"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item }}"
|
||||
checksum_algorithm: sha1
|
||||
loop: "{{ nomad_certificates_reload_watchlist }}"
|
||||
when: nomad_enable_tls
|
||||
register: _nomad_initial_cert_checksums
|
||||
|
||||
- name: "Nomad | Normalize initial checksums"
|
||||
ansible.builtin.set_fact:
|
||||
# This needs to be optimized, but I have spent so much time on it not
|
||||
# working that I will keep it as is for now, and we'll see later.
|
||||
_nomad_initial_checksums_normalized: >-
|
||||
{% filter trim %}
|
||||
{% set checksums = [] %}
|
||||
{% for item in _nomad_initial_cert_checksums.results %}
|
||||
{% set _ = checksums.append({
|
||||
'item': item.item,
|
||||
'initial_checksum': (item.stat.checksum | default('absent'))
|
||||
}) %}
|
||||
{% endfor %}
|
||||
{{ checksums }}
|
||||
{% endfilter %}
|
||||
when: nomad_enable_tls
|
||||
|
||||
- name: "Nomad | Copy extra configuration files"
|
||||
when: nomad_extra_files
|
||||
block:
|
||||
@ -72,3 +97,44 @@
|
||||
loop_control:
|
||||
loop_var: dir_source_item
|
||||
when: _nomad_dir_sources is defined
|
||||
|
||||
- name: "Nomad | Gather final checksums for certificate files"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item }}"
|
||||
checksum_algorithm: sha1
|
||||
loop: "{{ nomad_certificates_reload_watchlist }}"
|
||||
when: nomad_enable_tls
|
||||
register: _nomad_final_cert_checksums
|
||||
|
||||
- name: "Nomad | Normalize final checksums"
|
||||
ansible.builtin.set_fact:
|
||||
# This needs to be optimized, but I have spent so much time on it not
|
||||
# working that I will keep it as is for now, and we'll see later.
|
||||
_nomad_final_checksums_normalized: >-
|
||||
{% filter trim %}
|
||||
{% set checksums = [] %}
|
||||
{% for item in _nomad_final_cert_checksums.results %}
|
||||
{% set _ = checksums.append({
|
||||
'item': item.item,
|
||||
'final_checksum': (item.stat.checksum | default('absent'))
|
||||
}) %}
|
||||
{% endfor %}
|
||||
{{ checksums }}
|
||||
{% endfilter %}
|
||||
when: nomad_enable_tls
|
||||
|
||||
- name: "Nomad | Merge initial and final checksum lists"
|
||||
ansible.builtin.set_fact:
|
||||
_nomad_checksums_list: >-
|
||||
{{
|
||||
_nomad_initial_checksums_normalized |
|
||||
community.general.lists_mergeby(_nomad_final_checksums_normalized, 'item')
|
||||
}}
|
||||
when: nomad_enable_tls
|
||||
|
||||
- name: "Nomad | Determine if certificates have changed or were newly added"
|
||||
ansible.builtin.set_fact:
|
||||
_nomad_service_need_reload: true
|
||||
when:
|
||||
- nomad_enable_tls
|
||||
- _nomad_checksums_list | json_query('[?initial_checksum!=final_checksum]') | list| length > 0
|
||||
|
@ -136,6 +136,6 @@
|
||||
|
||||
- name: "Nomad | Set reload-check & restart-check variable"
|
||||
ansible.builtin.set_fact:
|
||||
_nomad_service_need_reload: true
|
||||
_nomad_service_need_daemon_reload: true
|
||||
_nomad_service_need_restart: true
|
||||
when: _nomad_unit_file.changed # noqa: no-handler
|
||||
|
@ -2,6 +2,7 @@
|
||||
# task/main file for nomad
|
||||
- name: "Nomad | Set reload-check & restart-check variable"
|
||||
ansible.builtin.set_fact:
|
||||
_nomad_service_need_daemon_reload: false
|
||||
_nomad_service_need_reload: false
|
||||
_nomad_service_need_restart: false
|
||||
|
||||
@ -33,11 +34,23 @@
|
||||
- name: "Nomad | Reload systemd daemon"
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
when: _nomad_service_need_reload
|
||||
when: _nomad_service_need_daemon_reload
|
||||
|
||||
- name: "Nomad | Start service: {{ nomad_service_name }}"
|
||||
ansible.builtin.service:
|
||||
name: "{{ nomad_service_name }}"
|
||||
state: restarted
|
||||
throttle: 1
|
||||
when: _nomad_service_need_restart
|
||||
when:
|
||||
- nomad_start_service
|
||||
- _nomad_service_need_restart
|
||||
|
||||
- name: "Nomad | Reload service: {{ nomad_service_name }}"
|
||||
ansible.builtin.service:
|
||||
name: "{{ nomad_service_name }}"
|
||||
state: reloaded
|
||||
throttle: 1
|
||||
when:
|
||||
- nomad_start_service
|
||||
- _nomad_service_need_reload
|
||||
- not _nomad_service_need_restart
|
||||
|
@ -15,6 +15,18 @@ nomad_github_project: hashicorp/nomad
|
||||
nomad_github_url: https://github.com
|
||||
nomad_repository_url: https://releases.hashicorp.com/nomad
|
||||
|
||||
nomad_certificates_reload_watchlist: |
|
||||
{% filter trim %}
|
||||
{% set watchlist = [] %}
|
||||
{% set config = nomad_configuration.get('tls', {}) %}
|
||||
{% for key in ['ca_file', 'cert_file', 'key_file'] %}
|
||||
{% if config.get(key) %}
|
||||
{{ watchlist.append(config[key]) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ watchlist | unique }}
|
||||
{% endfilter %}
|
||||
|
||||
nomad_configuration:
|
||||
datacenter: "{{ nomad_datacenter }}"
|
||||
region: "{{ nomad_region }}"
|
||||
|
@ -2,7 +2,7 @@
|
||||
# meta file for vault
|
||||
galaxy_info:
|
||||
namespace: "ednz_cloud"
|
||||
role_name: "hashicorp_vault"
|
||||
role_name: "vault"
|
||||
author: "Bertrand Lanson"
|
||||
description: "Install and configure hashicorp vault for debian-based distros."
|
||||
license: "license (BSD, MIT)"
|
||||
|
@ -24,6 +24,31 @@
|
||||
when: _vault_env_file.changed or
|
||||
_vault_config_file.changed
|
||||
|
||||
- name: "Vault | Gather initial checksums for certificate files"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item }}"
|
||||
checksum_algorithm: sha1
|
||||
loop: "{{ vault_certificates_reload_watchlist }}"
|
||||
when: vault_enable_tls
|
||||
register: _vault_initial_cert_checksums
|
||||
|
||||
- name: "Vault | Normalize initial checksums"
|
||||
ansible.builtin.set_fact:
|
||||
# This needs to be optimized, but I have spent so much time on it not
|
||||
# working that I will keep it as is for now, and we'll see later.
|
||||
_vault_initial_checksums_normalized: >-
|
||||
{% filter trim %}
|
||||
{% set checksums = [] %}
|
||||
{% for item in _vault_initial_cert_checksums.results %}
|
||||
{% set _ = checksums.append({
|
||||
'item': item.item,
|
||||
'initial_checksum': (item.stat.checksum | default('absent'))
|
||||
}) %}
|
||||
{% endfor %}
|
||||
{{ checksums }}
|
||||
{% endfilter %}
|
||||
when: vault_enable_tls
|
||||
|
||||
- name: "Vault | Copy extra configuration files"
|
||||
when: vault_extra_files
|
||||
block:
|
||||
@ -72,3 +97,44 @@
|
||||
loop_control:
|
||||
loop_var: dir_source_item
|
||||
when: _vault_dir_sources is defined
|
||||
|
||||
- name: "Vault | Gather final checksums for certificate files"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item }}"
|
||||
checksum_algorithm: sha1
|
||||
loop: "{{ vault_certificates_reload_watchlist }}"
|
||||
when: vault_enable_tls
|
||||
register: _vault_final_cert_checksums
|
||||
|
||||
- name: "Vault | Normalize final checksums"
|
||||
ansible.builtin.set_fact:
|
||||
# This needs to be optimized, but I have spent so much time on it not
|
||||
# working that I will keep it as is for now, and we'll see later.
|
||||
_vault_final_checksums_normalized: >-
|
||||
{% filter trim %}
|
||||
{% set checksums = [] %}
|
||||
{% for item in _vault_final_cert_checksums.results %}
|
||||
{% set _ = checksums.append({
|
||||
'item': item.item,
|
||||
'final_checksum': (item.stat.checksum | default('absent'))
|
||||
}) %}
|
||||
{% endfor %}
|
||||
{{ checksums }}
|
||||
{% endfilter %}
|
||||
when: vault_enable_tls
|
||||
|
||||
- name: "Vault | Merge initial and final checksum lists"
|
||||
ansible.builtin.set_fact:
|
||||
_vault_checksums_list: >-
|
||||
{{
|
||||
_vault_initial_checksums_normalized |
|
||||
community.general.lists_mergeby(_vault_final_checksums_normalized, 'item')
|
||||
}}
|
||||
when: vault_enable_tls
|
||||
|
||||
- name: "Vault | Determine if certificates have changed or were newly added"
|
||||
ansible.builtin.set_fact:
|
||||
_vault_service_need_reload: true
|
||||
when:
|
||||
- vault_enable_tls
|
||||
- _vault_checksums_list | json_query('[?initial_checksum!=final_checksum]') | list| length > 0
|
||||
|
@ -137,7 +137,7 @@
|
||||
|
||||
- name: "Vault | Set reload-check & restart-check variable"
|
||||
ansible.builtin.set_fact:
|
||||
_vault_service_need_reload: true
|
||||
_vault_service_need_daemon_reload: true
|
||||
_vault_service_need_restart: true
|
||||
when: _vault_unit_file.changed # noqa: no-handler
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
# task/main file for vault
|
||||
- name: "Vault | Set reload-check & restart-check variable"
|
||||
ansible.builtin.set_fact:
|
||||
_vault_service_need_daemon_reload: false
|
||||
_vault_service_need_reload: false
|
||||
_vault_service_need_restart: false
|
||||
|
||||
@ -33,13 +34,24 @@
|
||||
- name: "Vault | Reload systemd daemon"
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
when: _vault_service_need_reload
|
||||
when: _vault_service_need_daemon_reload
|
||||
|
||||
- name: "Vault | Start service: {{ vault_service_name }}"
|
||||
ansible.builtin.include_tasks: rolling_restart.yml
|
||||
when:
|
||||
- vault_start_service
|
||||
- _vault_service_need_restart
|
||||
- "hostvars[host_item].inventory_hostname == inventory_hostname"
|
||||
with_items: "{{ ansible_play_batch }}"
|
||||
loop_control:
|
||||
loop_var: host_item
|
||||
|
||||
- name: "Vault | Reload service: {{ vault_service_name }}"
|
||||
ansible.builtin.service:
|
||||
name: "{{ vault_service_name }}"
|
||||
state: reloaded
|
||||
throttle: 1
|
||||
when:
|
||||
- vault_start_service
|
||||
- _vault_service_need_reload
|
||||
- not _vault_service_need_restart
|
||||
|
@ -15,6 +15,22 @@ vault_github_project: hashicorp/vault
|
||||
vault_github_url: https://github.com
|
||||
vault_repository_url: https://releases.hashicorp.com/vault
|
||||
|
||||
vault_certificates_reload_watchlist: |
|
||||
{% filter trim %}
|
||||
{% set watchlist = [] %}
|
||||
{% for listener in vault_configuration.listener %}
|
||||
{% if listener.get('tcp') %}
|
||||
{% for key in ['tls_key_file', 'tls_cert_file', 'tls_client_ca_file'] %}
|
||||
{% if listener['tcp'].get(key) %}
|
||||
{{ watchlist.append(listener['tcp'][key]) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ watchlist | unique }}
|
||||
{% endfilter %}
|
||||
|
||||
|
||||
vault_configuration:
|
||||
cluster_name: "{{ vault_cluster_name }}"
|
||||
cluster_addr: "{{ 'https' if vault_enable_tls else 'http'}}://{{ vault_cluster_addr }}:8201"
|
||||
|
Loading…
Reference in New Issue
Block a user