Feature/dev 302

master
Görz, Friedrich 4 years ago committed by Ketelsen, Sven
parent 37d94b5166
commit d1b41daa87

@ -57,3 +57,68 @@ harbor_base_configuration:
oidc_verify_cert: true
oidc_auto_onboard: true
oidc_admin_group: '/admin'
scan_all_policy:
parameter:
daily_time: 0
project_object_template:
project_attributes:
project_name: '{{ elem }}'
meta_data:
auto_scan: true
project_state: present
members:
-
group_name: '/{{ elem }}'
group_type: oidc
role: projectadmin
harbor_projects_smardigo_default:
- awx
- sensw
- smardigo
harbor_projects: []
harbor_robot_tokens:
-
# secret_refresh: True
# token_state: present
name: ansible
level: system
description: 'smardigo docker pull credentials'
secret: '{{ docker_registry_token }}'
disable: false
duration: -1
editable: true
expires_at: -1
permissions:
- access:
- action: push
resource: repository
- action: pull
resource: repository
- action: delete
resource: artifact
- action: read
resource: helm-chart
- action: create
resource: helm-chart-version
- action: delete
resource: helm-chart-version
- action: create
resource: tag
- action: delete
resource: tag
- action: create
resource: artifact-label
- action: create
resource: scan
kind: project
namespace: "*"
harbor_scanall:
-
schedule:
cron: 0 0 1 * * *
type: Custom

@ -0,0 +1,80 @@
---
- name: "harbor BASE settings"
block:
- name: "BLOCK: Login with keycloak-admin"
include_role:
name: keycloak
tasks_from: _authenticate
- name: "GET available clients from <<{{ harbor_base_configuration.oidc_name }}>>-realm"
delegate_to: localhost
become: False
uri:
url: "{{ keycloak_server_url }}/auth/admin/realms/{{ harbor_base_configuration.oidc_name }}/clients"
method: GET
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ access_token }}"
status_code: [200]
register: realm_clients
# available clients: get needed ID
- set_fact:
id_of_client: '{{ ( realm_clients.json | selectattr("clientId","equalto", harbor_base_configuration.oidc_client_id ) | first ).id }}'
- name: "BLOCK: GET client-secret for client <<{{ harbor_base_configuration.oidc_client_id }}>> in realm <<{{ harbor_base_configuration.oidc_name }}>>"
delegate_to: localhost
become: False
uri:
url: "{{ keycloak_server_url }}/auth/admin/realms/{{ harbor_base_configuration.oidc_name }}/clients/{{ id_of_client }}/client-secret"
method: GET
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ access_token }}"
status_code: [200]
register: client_secret
- set_fact:
dict:
oidc_client_secret: '{{ client_secret.json.value }}'
- set_fact:
harbor_base_configuration_merged: '{{ harbor_base_configuration | combine( dict ,recursive=True ) }}'
- name: "BLOCK: Configure harbor BASE settings"
include_tasks: configure_base_config.yml
vars:
base_configuration: '{{ harbor_base_configuration_merged }}'
args:
apply:
tags:
- harbor-configure-base
# end of block for base settings
- name: "Create object of templated harbor projects"
set_fact:
projects_templated: "{{ ( projects_templated | default([]) ) + [ project_object_template ] }}"
loop: '{{ harbor_projects_smardigo_default }}'
loop_control:
loop_var: elem
when:
- harbor_projects_smardigo_default is defined
- name: "CRUD - projects"
include_tasks: configure_projects.yml
loop: '{{ harbor_projects + projects_templated }}'
loop_control:
loop_var: project
- name: "CRUD - robot tokens"
include_tasks: configure_robot_tokens.yml
loop: '{{ harbor_robot_tokens }}'
loop_control:
loop_var: robot_token
- name: "CRUD - scanall schedule"
include_tasks: configure_scanall_schedule.yml
loop: '{{ harbor_scanall }}'
loop_control:
loop_var: scanschedule

@ -0,0 +1,85 @@
---
- name: "Check if project <<{{ project.project_attributes.project_name }}>> exists"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project.project_attributes.project_name }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: GET
body_format: json
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200,403]
register: project_exists
delay: 10
retries: 3
- debug:
msg: 'found projects: {{ project_exists.json }}'
when: debug
- name: "Create project: <<{{ project.project_attributes.project_name }}>>"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: POST
body_format: json
body: '{{ project.project_attributes | to_json }}'
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200,201]
register: create_project
delay: 10
retries: 3
until: create_project.status in [200,201]
when:
- project_exists.status in [403]
- name: "Update project: <<{{ project.project_attributes.project_name }}>>"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project.project_attributes.project_name }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: PUT
body_format: json
body: '{{ project.project_attributes | to_json }}'
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200,201]
register: update_project
delay: 10
retries: 3
until: update_project.status in [200,201]
when:
- project_exists.status in [200]
- name: "Delete project: <<{{ project.project_attributes.project_name }}>>"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project.project_attributes.project_name }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: DELETE
body_format: json
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200]
register: create_project
delay: 10
retries: 3
until: create_project.status in [200]
when:
- project_exists.status in [200]
- project.project_state == 'absent'

@ -0,0 +1,137 @@
---
- set_fact:
member_state: '{{ member.member_state | default("present") }}'
harbor_member_roles:
-
name: projectadmin
role_id: 1
-
name: developer
role_id: 2
-
name: guest
role_id: 3
-
name: maintainer
role_id: 4
harbor_member_grouptypes:
-
name: ldap
group_type: 1
-
name: http
group_type: 2
-
name: oidc
group_type: 3
- name: "Get all project members"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project_name }}/members"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: GET
body_format: json
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200]
register: all_project_members
delay: 10
retries: 3
- set_fact:
group_type: "{{ ( harbor_member_grouptypes | selectattr('name','==',( member.group_type | lower )) | list | first ).group_type }}"
role_id: "{{ ( harbor_member_roles | selectattr('name','==',( member.role| lower ) ) | list | first ).role_id | int }}"
# creating body manual due to problems with IDs as integer - they will be converted to string in json
# => every API request will fail
# see also:
# https://stackoverflow.com/questions/69677986/converting-string-to-integer-in-ansible
- name: "Create membership"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project_name }}/members"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: POST
body_format: json
body: >-
{{
(
{
"role_id": role_id | int,
"member_group": {
"group_name": member.group_name,
"group_type": group_type | int
}
}
) | to_json }}
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200,201]
register: create_project_member
delay: 10
retries: 3
until: create_project_member.status in [200,201]
when:
- all_project_members.json | selectattr('entity_name','equalto',member.group_name) | list | length == 0
- member_state == 'present'
- name: "Update member: <<{{ member.group_name }}>>"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project_name }}/members/{{ ( all_project_members.json | selectattr('entity_name','equalto',member.group_name) | list | first ).id }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: PUT
body_format: json
body: >-
{{
(
{
"role_id": role_id | int,
"member_group": {
"group_name": member.group_name,
"group_type": group_type | int
}
}
) | to_json }}
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200,201]
register: update_project_member
delay: 10
retries: 3
until: update_project_member.status in [200,201]
when:
- all_project_members.json | selectattr('entity_name','equalto',member.group_name) | list | length == 1
- member_state == 'present'
- name: "Delete member: <<{{ member.group_name }}>>"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project_name }}/members/{{ ( all_project_members.json | selectattr('entity_name','equalto',member.group_name) | list | first ).id }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: DELETE
body_format: json
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200,201]
register: delete_project_member
delay: 10
retries: 3
until: delete_project_member.status in [200,201]
when:
- all_project_members.json | selectattr('entity_name','equalto',member.group_name) | list | length == 1
- member_state == 'absent'

@ -0,0 +1,65 @@
---
- name: "Get all meta_data"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project_name }}/metadatas/{{ meta_data_elem.key }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: GET
body_format: json
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200]
register: all_metadata
delay: 10
retries: 3
- set_fact:
body_content: "{ \"{{ meta_data_elem.key }}\":\"{{ meta_data_elem.value }}\" }"
- name: "Add meta_data: <<{{ meta_data_elem.key }}>>"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project_name }}/metadatas"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: POST
body_format: json
body: '{{ body_content }}'
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200,201]
register: create_metadata
delay: 10
retries: 3
until: create_metadata.status in [200,201]
when:
- meta_data_elem.key not in all_metadata.json
- name: "Update meta_data: <<{{ meta_data_elem.key }}>>"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/projects/{{ project_name }}/metadatas/{{ meta_data_elem.key }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: PUT
body_format: json
body: '{{ body_content }}'
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200,201]
register: update_metadata
delay: 10
retries: 3
until: update_metadata.status in [200,201]
when:
- meta_data_elem.key in all_metadata.json
# DELETION currently out-of-scope

@ -0,0 +1,22 @@
---
- name: "include CRUD for projects"
include_tasks: configure_project_crud.yml
- name: "include CRUD for project meta-data"
include_tasks: configure_project_metadata_crud.yml
vars:
project_name: '{{ project.project_attributes.project_name }}'
loop: '{{ project.meta_data | dict2items }}'
loop_control:
loop_var: meta_data_elem
when:
- project.meta_data is defined
- name: "include CRUD for project members"
include_tasks: configure_project_members_crud.yml
vars:
project_name: '{{ project.project_attributes.project_name }}'
loop: '{{ project.members }}'
loop_control:
loop_var: member

@ -0,0 +1,20 @@
---
- set_fact:
tok_obj: {}
- debug:
msg: "DEBUGGING - robot_token: {{ robot_token }}"
when:
- debug
- name: "Drop token_state from dict to avoid rejecting object by harbor API due to unknown field"
set_fact:
tok_obj: "{{ tok_obj |combine({item.key: item.value})}}"
when: item.key not in ['token_state']
with_dict: "{{ robot_token }}"
- name:
include_tasks: configure_robot_tokens_crud.yml
vars:
token_state: "{{ robot_token.token_state | default('present') }}"
token_object: "{{ tok_obj }}"

@ -0,0 +1,174 @@
---
- set_fact:
token_object_combined: {}
- name: "Get all robot tokens"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/robots"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: GET
body_format: json
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200]
register: all_robot_tokens
delay: 10
retries: 3
- name: "Create robot token"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/robots"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: POST
body_format: json
body: '{{ token_object | to_json }}'
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200,201]
register: create
delay: 10
retries: 3
until: create.status in [200,201]
when:
- all_robot_tokens.json | selectattr('name','contains',token_object.name) | list | length == 0
- token_state == 'present'
- set_fact:
robots_id: "{{ ( all_robot_tokens.json | selectattr('name','contains',token_object.name) | list | first ).id }}"
remote_robot_token_object: "{{ all_robot_tokens.json | selectattr('name','contains',token_object.name) | list | first }}"
token_object_combined: "{{ all_robot_tokens.json | selectattr('name','contains',token_object.name) | list | first | combine(token_object, recursive=True) }}"
token_object_dropped: {}
when:
- all_robot_tokens.json | selectattr('name','contains',token_object.name) | list | length == 1
- name: "Refresh the robot secret"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/robots/{{ robots_id }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: PATCH
body_format: json
body: >-
{{
(
{
"secret": token_object.secret
}
)
}}
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200]
register: update
delay: 10
retries: 3
until: update.status in [200]
when:
- all_robot_tokens.json | selectattr('name','contains',token_object.name) | list | length == 1
- token_state == 'present'
- token_object.secret_refresh is defined
- token_object.secret_refresh
- name: "Block to Update robot token data"
block:
- debug:
msg: "DEBUGGING before dropping - combined token_object_combined: {{ token_object_combined }}"
when:
- debug
# unknown param/key in object robot-token will result in errors with harbor API
# therefore we drop $keys from dict
- name: "Drop some keys from updated robot token object"
set_fact:
token_object_dropped: "{{ token_object_dropped | combine({item.key: item.value})}}"
with_dict: "{{ token_object_combined }}"
when: "{{ item.key not in ['secret','secret_refresh'] }}"
# harbor API behaviour:
# in case of initial creation for robot token objects, harbor creates a name for this
# in form of << robot$OBJECT_NAME >> - plz be aware of the dollar sign!
# but only the OBJECT_NAME was defined in object declaration.
# In case of updating we have to make sure that the << robot$OBJECT_NAME >> is used in the
# updated object thrown against harbor API.
#
# so harbor API forces me to create this workaround to avoid such errors
#
# part 1: define name of object
- set_fact:
robot_token_name_cleaned:
name: 'robot${{ token_object_dropped.name }}'
# part 2: override name with new defined name of object
- set_fact:
token_object_finished: '{{ token_object_dropped | combine(robot_token_name_cleaned, recursive=True) }}'
- debug:
msg: "DEBUGGING after dropping - combined token_object_finished: {{ token_object_finished }}"
when:
- debug
# to update a robot token, the following conditions must be satisfied
# 1. ALL params of robot token object must be set
# 1.1. except the secret param - it must be removed/rejected from object - it will be updated with PATCH-method instead of PUT-method
# 2. the update (of parameter) itself
#
# there is no possibility to update if one of mentioned conditions is not statisfied.
# the API call will fail with one of the following errors:
# - HTTP 400 - "cannot update the level or name of robot"
# - HTTP 400 - "bad request error level input:"
#
- name: "Update robot token object"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/robots/{{ robots_id }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: PUT
body_format: json
body: '{{ token_object_finished | to_json }}'
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200]
register: update
delay: 10
retries: 3
until: update.status in [200]
# when - part of BLOCK-statement
when:
- all_robot_tokens.json | selectattr('name','contains',token_object.name) | list | length == 1
- token_state == 'present'
# end of BLOCK to Update robot token data
- name: "Delete robot token"
delegate_to: 127.0.0.1
become: false
uri:
url: "{{ harbor_external_url }}/api/v2.0/robots/{{ robots_id }}"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: DELETE
body_format: json
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200]
register: delete_project_member
delay: 10
retries: 3
until: delete_project_member.status in [200]
when:
- all_robot_tokens.json | selectattr('name','contains',token_object.name) | list | length == 1
- token_state == 'absent'

@ -0,0 +1,30 @@
---
- name: "configure | configure scanall schedule | CREATE scanschedule"
uri:
url: "{{ harbor_external_url }}/api/v2.0/system/scanAll/schedule"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: POST
body_format: json
force_basic_auth: yes
headers:
Content-Type: application/json
status_code: [200]
body: '{{ scanschedule |to_json }}'
status_code: [201,412]
register: create_scanschedule
- name: "configure | configure scanall schedule | UPDATE scanschedule"
uri:
url: "{{ harbor_external_url }}/api/v2.0/system/scanAll/schedule"
user: '{{ harbor_admin_username }}'
password: '{{ harbor_admin_password }}'
method: PUT
body_format: json
force_basic_auth: yes
headers:
Content-Type: application/json
body: '{{ scanschedule |to_json }}'
status_code: [200]
when:
- create_scanschedule.status in [412]

@ -7,54 +7,9 @@
tags:
- harbor-install
- name: "harbor BASE settings"
block:
- name: "BLOCK: Login with keycloak-admin"
include_role:
name: keycloak
tasks_from: _authenticate
- name: "GET available clients from <<{{ harbor_base_configuration.oidc_name }}>>-realm"
delegate_to: localhost
become: False
uri:
url: "{{ keycloak_server_url }}/auth/admin/realms/{{ harbor_base_configuration.oidc_name }}/clients"
method: GET
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ access_token }}"
status_code: [200]
register: realm_clients
# available clients: get needed ID
- set_fact:
id_of_client: '{{ ( realm_clients.json | selectattr("clientId","equalto", harbor_base_configuration.oidc_client_id ) | first ).id }}'
- name: "BLOCK: GET client-secret for client <<{{ harbor_base_configuration.oidc_client_id }}>> in realm <<{{ harbor_base_configuration.oidc_name }}>>"
delegate_to: localhost
become: False
uri:
url: "{{ keycloak_server_url }}/auth/admin/realms/{{ harbor_base_configuration.oidc_name }}/clients/{{ id_of_client }}/client-secret"
method: GET
headers:
Content-Type: "application/json"
Authorization: "Bearer {{ access_token }}"
status_code: [200]
register: client_secret
- set_fact:
dict:
oidc_client_secret: '{{ client_secret.json.value }}'
- set_fact:
harbor_base_configuration_merged: '{{ harbor_base_configuration | combine( dict ,recursive=True ) }}'
- name: "BLOCK: Configure harbor BASE settings"
include_tasks: configure_base_config.yml
vars:
base_configuration: '{{ harbor_base_configuration_merged }}'
- name: "Configure harbor"
include_tasks: configure.yml
args:
apply:
tags:
- harbor-configure-base
# end of block for base settings
- harbor-configure

Loading…
Cancel
Save