setup gitea as oidc provider for tool vault
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
|||||||
.terraform
|
.terraform
|
||||||
|
.terraform.*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
@@ -58,7 +58,8 @@ issues: http://example.com/issue/tracker
|
|||||||
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
|
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
|
||||||
# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
|
# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
|
||||||
# and '.git' are always filtered. Mutually exclusive with 'manifest'
|
# and '.git' are always filtered. Mutually exclusive with 'manifest'
|
||||||
build_ignore: []
|
build_ignore:
|
||||||
|
- playwright/
|
||||||
|
|
||||||
# A dict controlling use of manifest directives used in building the collection artifact. The key 'directives' is a
|
# A dict controlling use of manifest directives used in building the collection artifact. The key 'directives' is a
|
||||||
# list of MANIFEST.in style
|
# list of MANIFEST.in style
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
- name: hashicorp_vault
|
||||||
|
# hosts: raspberries:&local
|
||||||
|
hosts: localhost
|
||||||
|
# debugger: on_failed
|
||||||
|
|
||||||
|
vars_prompt:
|
||||||
|
- name: gitea_admin_password
|
||||||
|
prompt: Enter gitea admin password
|
||||||
|
unsafe: true # password can contain uncommon chars such as '{'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
- name: Setup Hashicorp Vault
|
||||||
|
include_role:
|
||||||
|
name: hashicorp_vault
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
- name: pgbouncer
|
|
||||||
hosts: raspberries:&local
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- ansible.builtin.ping:
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
- name: prometheus
|
|
||||||
hosts: raspberries:&local
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- ansible.builtin.ping:
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
vault_unseal_keys_path: ~/.arcodange/cluster-keys.json
|
||||||
|
vault_unseal_keys_shares: 1
|
||||||
|
vault_unseal_keys_key_threshold: 1 # keys_key_threshold <= keys_shares
|
||||||
|
|
||||||
|
vault_address: https://vault.arcodange.duckdns.org
|
||||||
|
|
||||||
|
vault_oidc_gitea_setupGiteaAppJS: '{{ role_path }}/files/playwright_setupGiteaApp.js'
|
||||||
|
|
||||||
|
gitea_admin_user: arcodange@gmail.com
|
||||||
|
gitea_admin_password: "{{ undef(hint='You must specify the gitea user admin password') }}"
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
terraform {
|
||||||
|
backend "gcs" {
|
||||||
|
bucket = "arcodange-tf"
|
||||||
|
prefix = "tools/hashicorp_vault/gitea_oidc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "vault_address" {
|
||||||
|
type = string
|
||||||
|
default = "http://127.0.0.1:8200"
|
||||||
|
}
|
||||||
|
variable "vault_token" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
variable "admin_email" {
|
||||||
|
type = string
|
||||||
|
default = "arcodange@gmail.com"
|
||||||
|
}
|
||||||
|
variable "gitea_app" {
|
||||||
|
type = object({
|
||||||
|
url = optional(string, "https://gitea.arcodange.duckdns.org/")
|
||||||
|
id = string
|
||||||
|
secret = string
|
||||||
|
description = optional(string, "Arcodange Gitea Auth")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
vault = {
|
||||||
|
source = "vault"
|
||||||
|
version = "4.4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider vault {
|
||||||
|
address = var.vault_address
|
||||||
|
token = var.vault_token
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "vault_jwt_auth_backend" "gitea" {
|
||||||
|
description = var.gitea_app.description
|
||||||
|
default_role = "default"
|
||||||
|
path = "gitea"
|
||||||
|
type = "oidc"
|
||||||
|
oidc_discovery_url = var.gitea_app.url
|
||||||
|
oidc_client_id = var.gitea_app.id
|
||||||
|
oidc_client_secret = var.gitea_app.secret
|
||||||
|
bound_issuer = var.gitea_app.url
|
||||||
|
tune {
|
||||||
|
allowed_response_headers = []
|
||||||
|
audit_non_hmac_request_keys = []
|
||||||
|
audit_non_hmac_response_keys = []
|
||||||
|
default_lease_ttl = "768h"
|
||||||
|
listing_visibility = "unauth"
|
||||||
|
max_lease_ttl = "768h"
|
||||||
|
passthrough_request_headers = []
|
||||||
|
token_type = "default-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "vault_jwt_auth_backend_role" "gitea" {
|
||||||
|
backend = vault_jwt_auth_backend.gitea.path
|
||||||
|
role_name = "default"
|
||||||
|
token_policies = ["default"]
|
||||||
|
|
||||||
|
user_claim = "email"
|
||||||
|
role_type = "oidc"
|
||||||
|
allowed_redirect_uris = [
|
||||||
|
"${var.vault_address}/ui/vault/auth/gitea/oidc/callback",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
data "vault_policy_document" "admin" {
|
||||||
|
rule {
|
||||||
|
path = "*"
|
||||||
|
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
|
||||||
|
description = "admin privileges"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resource "vault_policy" "admin" {
|
||||||
|
name = "admin"
|
||||||
|
policy = data.vault_policy_document.admin.hcl
|
||||||
|
}
|
||||||
|
resource "vault_identity_entity" "admin" {
|
||||||
|
name = var.admin_email
|
||||||
|
policies = [vault_policy.admin.name]
|
||||||
|
}
|
||||||
|
resource "vault_identity_entity_alias" "admin" {
|
||||||
|
name = var.admin_email
|
||||||
|
mount_accessor = vault_jwt_auth_backend.gitea.accessor
|
||||||
|
canonical_id = vault_identity_entity.admin.id
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { chromium } from 'playwright';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initialisation
|
||||||
|
*/
|
||||||
|
const username = process.env.GITEA_USER;
|
||||||
|
const password = process.env.GITEA_PASSWORD;
|
||||||
|
const debug = Boolean(process.env.DEBUG);
|
||||||
|
const vaultAddress = process.env.VAULT_ADDRESS || 'http://localhost:8200';
|
||||||
|
const giteaAddress = process.env.GITEA_ADDRESS || 'https://gitea.arcodange.duckdns.org';
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
console.error('Veuillez définir les variables d\'environnement GITEA_USER et GITEA_PASSWORD.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const browser = await chromium.launch({
|
||||||
|
headless: true,
|
||||||
|
locale: "gb-GB", // before login gitea use gb-GB locale, after login it choose user locale
|
||||||
|
logger: {
|
||||||
|
isEnabled: (name, severity) => debug,
|
||||||
|
log: (name, severity, message, args) => console.warn(`${severity}| ${name} :: ${message} __ ${args}`)
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const context = await browser.newContext({locale: "gb-GB"});
|
||||||
|
const page = await context.newPage();
|
||||||
|
|
||||||
|
async function doLogin() {
|
||||||
|
await page.goto(giteaAddress);
|
||||||
|
await page.click('text=Sign In');
|
||||||
|
await page.fill('input[name="user_name"]', username);
|
||||||
|
await page.fill('input[name="password"]', password);
|
||||||
|
await page.click('button:has-text("Sign In")');
|
||||||
|
await page.waitForURL(giteaAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isLoggedIn() {
|
||||||
|
return (
|
||||||
|
await page.locator('text=Sign In').count() === 0
|
||||||
|
&& await page.locator('.user-menu > .ui.header strong').count() > 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// async function getLoggedUsername() {
|
||||||
|
// const loggedInUser = await page.innerText('.user-menu > .ui.header strong');
|
||||||
|
// if (debug) console.warn(`Connecté en tant que : ${loggedInUser}`);
|
||||||
|
// return loggedInUser;
|
||||||
|
// }
|
||||||
|
|
||||||
|
async function setupApp() {
|
||||||
|
const appName = process.env.GITEA_APP_NAME || 'Arcodange Hashicorp Vault';
|
||||||
|
|
||||||
|
await page.goto(`${giteaAddress}/admin/applications`);
|
||||||
|
|
||||||
|
const applicationsPanel = await page.locator('.admin-setting-content');
|
||||||
|
const applicationNameClass = await applicationsPanel.getByText('Git Credential Manager').getAttribute('class');
|
||||||
|
const appNames = await applicationsPanel.locator(`.${applicationNameClass}`).allInnerTexts();
|
||||||
|
|
||||||
|
const app = {};
|
||||||
|
|
||||||
|
if (appNames.includes(appName)) {
|
||||||
|
console.warn('app found');
|
||||||
|
|
||||||
|
const appElem = await applicationsPanel.locator(`.${applicationNameClass}`).getByText(appName).locator('xpath=../..');
|
||||||
|
await appElem.highlight();
|
||||||
|
await appElem.locator('a.button').click();
|
||||||
|
|
||||||
|
await page.waitForURL( new RegExp(`${giteaAddress}/admin/applications/oauth2/\\d+$`) );
|
||||||
|
|
||||||
|
await applicationsPanel.locator('form[action$="/regenerate_secret"] > button').click();
|
||||||
|
} else {
|
||||||
|
console.warn('app not found');
|
||||||
|
await applicationsPanel.locator('input[name="application_name"]').fill(appName);
|
||||||
|
await applicationsPanel.locator('textarea[name="redirect_uris"]').fill([
|
||||||
|
`${vaultAddress}/ui/vault/auth/gitea/oidc/callback`
|
||||||
|
].join('\n'));
|
||||||
|
await applicationsPanel.locator('form[action="/admin/applications/oauth2"] > button').dblclick()
|
||||||
|
|
||||||
|
await page.waitForURL(`${giteaAddress}/admin/applications/oauth2`);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.id = await applicationsPanel.locator('input[id="client-id"]').getAttribute('value');
|
||||||
|
app.secret = await applicationsPanel.locator('input[id="client-secret"]').getAttribute('value');
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! await isLoggedIn()) await doLogin();
|
||||||
|
// let _ = await getLoggedUsername()
|
||||||
|
const app = await setupApp();
|
||||||
|
|
||||||
|
console.log(JSON.stringify(app));
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
# Usage: prompt for gitea_admin_password in the calling playbook
|
||||||
|
# vars_prompt:
|
||||||
|
# - name: gitea_admin_password
|
||||||
|
# prompt: Enter gitea admin password
|
||||||
|
# unsafe: true # password can contain uncommon chars such as '{'
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
playwright_script: '{{ vault_oidc_gitea_setupGiteaAppJS }}'
|
||||||
|
playwright_env:
|
||||||
|
GITEA_USER: '{{ gitea_admin_user }}'
|
||||||
|
GITEA_PASSWORD: '{{ gitea_admin_password }}'
|
||||||
|
VAULT_ADDRESS: '{{ vault_address }}'
|
||||||
|
|
||||||
|
- include_role:
|
||||||
|
name: arcodange.factory.playwright
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
gite_app: '{{ playwright_job.stdout | from_json }}'
|
||||||
|
|
||||||
|
volume_name: tofu-{{ ansible_date_time.iso8601.replace(':','-') }}
|
||||||
|
|
||||||
|
- name: use tofu to provision vault
|
||||||
|
block:
|
||||||
|
- shell: docker volume create {{ volume_name }}
|
||||||
|
- shell: >-
|
||||||
|
docker run --rm
|
||||||
|
-v {{ volume_name }}:/tofu -w /tofu
|
||||||
|
-v {{ role_path }}/files/hashicorp_vault.tf:/tofu/hashicorp_vault.tf
|
||||||
|
-v ~/.config/gcloud:/root/.config/gcloud
|
||||||
|
--entrypoint=''
|
||||||
|
ghcr.io/opentofu/opentofu:latest
|
||||||
|
{{ command }}
|
||||||
|
loop:
|
||||||
|
- tofu init -no-color
|
||||||
|
- >-
|
||||||
|
tofu apply -auto-approve -no-color
|
||||||
|
-var='gitea_app={{ gite_app | to_json }}'
|
||||||
|
-var='vault_address={{ vault_address }}'
|
||||||
|
-var='vault_token={{ vault_root_token }}'
|
||||||
|
loop_control:
|
||||||
|
loop_var: command
|
||||||
|
|
||||||
|
always:
|
||||||
|
- shell: docker volume rm {{ volume_name }}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
- name: list vault servers to unseal
|
||||||
|
ansible.builtin.command:
|
||||||
|
kubectl -n tools get pods -l app.kubernetes.io/instance=hashicorp-vault -l component=server -o jsonpath="{.items[*]['.metadata.name']}"
|
||||||
|
register: vault_servers_cmd
|
||||||
|
changed_when: False
|
||||||
|
|
||||||
|
- ansible.builtin.set_fact:
|
||||||
|
vault_servers: '{{ vault_servers_cmd.stdout.split() }}'
|
||||||
|
|
||||||
|
- ansible.builtin.shell:
|
||||||
|
kubectl -n tools exec {{ vault_servers[0] }} -- vault operator init -status
|
||||||
|
register: vault_init_status
|
||||||
|
failed_when: vault_init_status.rc == 1
|
||||||
|
changed_when: False
|
||||||
|
|
||||||
|
- when: vault_init_status.rc == 2
|
||||||
|
block:
|
||||||
|
- ansible.builtin.debug:
|
||||||
|
msg: not initialized
|
||||||
|
- ansible.builtin.command: >-
|
||||||
|
kubectl -n tools exec {{ vault_servers[0] }} -- vault operator init
|
||||||
|
-format=json
|
||||||
|
-key-shares={{ vault_unseal_keys_shares }}
|
||||||
|
-key-threshold={{ vault_unseal_keys_key_threshold }}
|
||||||
|
register: vault_keys_cmd
|
||||||
|
|
||||||
|
- ansible.builtin.file:
|
||||||
|
state: directory
|
||||||
|
path: '{{ vault_unseal_keys_path | dirname }}'
|
||||||
|
mode: '700'
|
||||||
|
- ansible.builtin.copy:
|
||||||
|
content: '{{ vault_keys_cmd.stdout }}'
|
||||||
|
dest: '{{ vault_unseal_keys_path }}'
|
||||||
|
mode: '600'
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
- name: Init (first time only)
|
||||||
|
include_tasks: init.yml
|
||||||
|
- name: Unseal (required on reboot)
|
||||||
|
include_tasks: unseal.yml
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: Generate root token
|
||||||
|
include_tasks: new_root_token.yml
|
||||||
|
- name: Setup gitea oidc auth
|
||||||
|
include_tasks: gitea_oidc_auth.yml
|
||||||
|
always:
|
||||||
|
- name: Revoke root token
|
||||||
|
include_tasks: revoke_token.yml
|
||||||
|
vars:
|
||||||
|
vault_token_to_revoke: '{{ vault_root_token }}'
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
- name: 'Generate New Root Token'
|
||||||
|
include_tasks: vault_cmd.yml
|
||||||
|
vars:
|
||||||
|
vault_cmd: '{{ item.cmd }}'
|
||||||
|
vault_cmd_output_var: '{{ item.save }}'
|
||||||
|
vault_cmd_json_attr: '{{ item.json_attr | default("") }}'
|
||||||
|
vault_cmd_can_fail: '{{ item.can_fail | default(false) }}'
|
||||||
|
|
||||||
|
vault_unseal_keys: "{{ (lookup('ansible.builtin.file', vault_unseal_keys_path) | from_json)['unseal_keys_b64'] }}"
|
||||||
|
cmds:
|
||||||
|
- cmd: vault token revoke -self
|
||||||
|
save: false
|
||||||
|
can_fail: true
|
||||||
|
- cmd: vault operator generate-root -generate-otp
|
||||||
|
save: vault_generate_root_otp
|
||||||
|
- cmd: !unsafe vault operator generate-root -cancel -otp {{ vault_generate_root_otp }}
|
||||||
|
- cmd: !unsafe vault operator generate-root -init -otp {{ vault_generate_root_otp }}
|
||||||
|
save: vault_generate_root_nonce
|
||||||
|
json_attr: 'nonce'
|
||||||
|
- |- # not yet tested with vault_unseal_keys_key_threshold > 1 (saving not available encoded_root_token might break)
|
||||||
|
{{
|
||||||
|
vault_unseal_keys[:vault_unseal_keys_key_threshold] | map('regex_replace', '(.+)', '{
|
||||||
|
"cmd": "vault operator generate-root -nonce {{ vault_generate_root_nonce }} \1",
|
||||||
|
"save": "vault_generate_root_encoded_token",
|
||||||
|
"json_attr": "encoded_root_token"
|
||||||
|
}') | map("from_json") | list
|
||||||
|
}}
|
||||||
|
- cmd: !unsafe vault operator generate-root -decode {{ vault_generate_root_encoded_token }} -otp {{ vault_generate_root_otp }}
|
||||||
|
save: vault_root_token
|
||||||
|
- cmd: !unsafe vault login {{ vault_root_token }}
|
||||||
|
save: false
|
||||||
|
loop: '{{ cmds | flatten }}'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
- shell: >-
|
||||||
|
kubectl exec -n tools hashicorp-vault-0 --
|
||||||
|
vault token revoke {{ vault_token_to_revoke | default('-self') }}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
- debug:
|
||||||
|
msg: reading from {{ vault_unseal_keys_path }}
|
||||||
|
- ansible.builtin.set_fact:
|
||||||
|
vault_keys: "{{ lookup('ansible.builtin.file', vault_unseal_keys_path) | from_json }}"
|
||||||
|
|
||||||
|
- ansible.builtin.command:
|
||||||
|
kubectl -n tools exec {{ vault_server__key[0] }} -- vault operator unseal {{ vault_server__key[1] }}
|
||||||
|
loop: '{{ vault_servers | product(vault_keys["unseal_keys_b64"]) }}'
|
||||||
|
loop_control:
|
||||||
|
loop_var: vault_server__key
|
||||||
|
label: 'unsealing {{ vault_server__key[0] }}'
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- ansible.builtin.command:
|
||||||
|
kubectl -n tools exec {{ vault_servers[0] }} -- vault login {{ vault_keys["root_token"] }}
|
||||||
|
|
||||||
|
- name: Revoke root token
|
||||||
|
include_tasks: revoke_token.yml
|
||||||
|
vars:
|
||||||
|
vault_token_to_revoke: '-self'
|
||||||
|
rescue:
|
||||||
|
- debug:
|
||||||
|
msg: 'initial root token already revoked'
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
- set_fact:
|
||||||
|
vault_cmd_interpolated: '{{ vault_cmd }}'
|
||||||
|
|
||||||
|
- name: 'variable interpolation in vault_cmd'
|
||||||
|
set_fact:
|
||||||
|
vault_cmd_interpolated: "{{ vault_cmd_interpolated | regex_replace('\\{\\{ *' + varname + ' *\\}\\}', lookup('vars', varname, default='')) }}"
|
||||||
|
loop: "{{ vault_cmd | regex_findall('\\{\\{ *([a-zA-Z_][a-zA-Z0-9_]*) *\\}\\}') }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: varname
|
||||||
|
|
||||||
|
- ansible.builtin.shell: >-
|
||||||
|
kubectl exec -n tools hashicorp-vault-0 -- {{ 'env VAULT_FORMAT=json' if vault_cmd_json_attr != '' else '' }}
|
||||||
|
{{ vault_cmd_interpolated }}
|
||||||
|
register: vault_cmd_output
|
||||||
|
ignore_errors: '{{ vault_cmd_can_fail }}'
|
||||||
|
|
||||||
|
|
||||||
|
- when: vault_cmd_output_var is not false
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
'{{ vault_cmd_output_var | default("vault_cmd_out") }}' : |-
|
||||||
|
{{ vault_cmd_output.stdout if not vault_cmd_json_attr else
|
||||||
|
(vault_cmd_output.stdout | from_json)[vault_cmd_json_attr]
|
||||||
|
}}
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
---
|
---
|
||||||
- name: pgbouncer
|
- name: hashicorp_vault
|
||||||
ansible.builtin.import_playbook: pgbouncer.yml
|
ansible.builtin.import_playbook: hashicorp_vault.yml
|
||||||
|
|
||||||
- name: prometheus
|
|
||||||
ansible.builtin.import_playbook: prometheus.yml
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
playwright_script: '{{ {{ role_path }}/files/loginGitea.js }}'
|
||||||
|
playwright_use_docker: true
|
||||||
|
playwright_version: '1.47.0'
|
||||||
|
playwright_docker_image: playwright:{{ playwright_version }}
|
||||||
25
ansible/arcodange/factory/roles/playwright/files/Dockerfile
Normal file
25
ansible/arcodange/factory/roles/playwright/files/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Utiliser l'image officielle Node.js avec Playwright
|
||||||
|
FROM mcr.microsoft.com/playwright:v1.38.0-jammy
|
||||||
|
|
||||||
|
ARG playwright_version=1.47.0
|
||||||
|
|
||||||
|
# Définir le répertoire de travail
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copier les fichiers package.json et package-lock.json
|
||||||
|
COPY v${playwright_version}/package*.json ./
|
||||||
|
|
||||||
|
# Installer les dépendances Node.js
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Installer les navigateurs nécessaires pour Playwright
|
||||||
|
RUN npx playwright install
|
||||||
|
|
||||||
|
# Copier le script par défaut
|
||||||
|
COPY loginGitea.js ./script.js
|
||||||
|
|
||||||
|
# Commande pour exécuter le script
|
||||||
|
CMD ["node", "script.js"]
|
||||||
|
|
||||||
|
# RUN WITH
|
||||||
|
# docker run -v $PWD/loginGitea.js:/app/loginGitea.js --rm playwright-gitea sh -c "sed 's/headless: false/headless: true/' loginGitea.js | node --input-type=module"
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { chromium } from 'playwright';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initialisation
|
||||||
|
*/
|
||||||
|
const username = process.env.GITEA_USER;
|
||||||
|
const password = process.env.GITEA_PASSWORD;
|
||||||
|
const debug = Boolean(process.env.DEBUG);
|
||||||
|
const vaultAddress = process.env.VAULT_ADDRESS || 'http://localhost:8200';
|
||||||
|
const giteaAddress = process.env.GITEA_ADDRESS || 'https://gitea.arcodange.duckdns.org';
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
console.error('Veuillez définir les variables d\'environnement GITEA_USER et GITEA_PASSWORD.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const browser = await chromium.launch({
|
||||||
|
headless: true,
|
||||||
|
locale: "gb-GB", // before login gitea use gb-GB locale, after login it choose user locale
|
||||||
|
logger: {
|
||||||
|
isEnabled: (name, severity) => debug,
|
||||||
|
log: (name, severity, message, args) => console.warn(`${severity}| ${name} :: ${message} __ ${args}`)
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const context = await browser.newContext({locale: "gb-GB"});
|
||||||
|
const page = await context.newPage();
|
||||||
|
|
||||||
|
async function doLogin() {
|
||||||
|
await page.goto(giteaAddress);
|
||||||
|
await page.click('text=Sign In');
|
||||||
|
await page.fill('input[name="user_name"]', username);
|
||||||
|
await page.fill('input[name="password"]', password);
|
||||||
|
await page.click('button:has-text("Sign In")');
|
||||||
|
await page.waitForURL(giteaAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isLoggedIn() {
|
||||||
|
return (
|
||||||
|
await page.locator('text=Sign In').count() === 0
|
||||||
|
&& await page.locator('.user-menu > .ui.header strong').count() > 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getLoggedUsername() {
|
||||||
|
const loggedInUser = await page.innerText('.user-menu > .ui.header strong');
|
||||||
|
if (debug) console.warn(`Connecté en tant que : ${loggedInUser}`);
|
||||||
|
return loggedInUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! await isLoggedIn()) await doLogin();
|
||||||
|
const giteaUser = await getLoggedUsername()
|
||||||
|
|
||||||
|
console.log(JSON.stringify({giteaUser}));
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
70
ansible/arcodange/factory/roles/playwright/files/v1.47.0/package-lock.json
generated
Normal file
70
ansible/arcodange/factory/roles/playwright/files/v1.47.0/package-lock.json
generated
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"name": "playwright",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "playwright",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright": "^1.47.0",
|
||||||
|
"typescript": "^5.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.47.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.0.tgz",
|
||||||
|
"integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.47.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.47.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.0.tgz",
|
||||||
|
"integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
||||||
|
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "playwright",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "loginGitea.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node loginGitea.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "arcodange@gmail.com",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright": "^1.47.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
25
ansible/arcodange/factory/roles/playwright/tasks/main.yml
Normal file
25
ansible/arcodange/factory/roles/playwright/tasks/main.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
- when: playwright_use_docker
|
||||||
|
block:
|
||||||
|
- name: Build {{ playwright_docker_image }} docker image
|
||||||
|
community.docker.docker_image_build:
|
||||||
|
name: '{{ playwright_docker_image }}'
|
||||||
|
path: '{{ role_path }}/files/'
|
||||||
|
args:
|
||||||
|
playwright_version: '{{ playwright_version }}'
|
||||||
|
- name: run {{ playwright_script | basename }}
|
||||||
|
vars:
|
||||||
|
cmd_env: '{{ playwright_env | default({}) }}'
|
||||||
|
env_arguments: >-
|
||||||
|
{% for e in (cmd_env.keys() | zip( cmd_env.values() ) | map('join', '=') ) %}
|
||||||
|
-e {{ e }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
ansible.builtin.shell: >-
|
||||||
|
docker run
|
||||||
|
-v {{ playwright_script }}:/app/script.js
|
||||||
|
{{ env_arguments }}
|
||||||
|
--rm
|
||||||
|
{{ playwright_docker_image }}
|
||||||
|
# sh -c "sed 's/headless: *false/headless: true/' script.js | node --input-type=module"
|
||||||
|
|
||||||
|
register: playwright_job
|
||||||
56
doc/adr/04_tool_hashicorp_vault.md
Normal file
56
doc/adr/04_tool_hashicorp_vault.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# [Bases](./README.md)
|
||||||
|
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
### Hashicorp Vault
|
||||||
|
|
||||||
|
>[!WARNING]
|
||||||
|
>L'unsealKey, le vaultRootToken initial et l'authentification au backend terraform sont pour le moment configurés sur le controleur ansible (Macbook Pro).
|
||||||
|
|
||||||
|
>[!NOTE]
|
||||||
|
> Vault est déployé via [argo cd](https://gitea.arcodange.duckdns.org/arcodange-org/tools/src/branch/main/hashicorp-vault)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
%%{init: { 'logLevel': 'debug', 'theme': 'base',
|
||||||
|
'sequence': {
|
||||||
|
'showSequenceNumbers': true,
|
||||||
|
'mirrorActors': false
|
||||||
|
}
|
||||||
|
}}%%
|
||||||
|
sequenceDiagram
|
||||||
|
participant Ansible
|
||||||
|
participant Gitea
|
||||||
|
participant Vault
|
||||||
|
|
||||||
|
Note right of Vault: Argo CD App <br> versioned in Gitea
|
||||||
|
|
||||||
|
rect rgb(191, 223, 255)
|
||||||
|
|
||||||
|
Ansible ->> Gitea : setupAdminAccount(adminPassword)
|
||||||
|
Ansible ->> Vault : init
|
||||||
|
activate Vault
|
||||||
|
Vault -->> Ansible : (unsealKey, vaultRootToken)
|
||||||
|
deactivate Vault
|
||||||
|
Ansible ->> Vault: unseal(unsealKey)
|
||||||
|
Ansible ->> Vault: revoke vaultRootToken
|
||||||
|
|
||||||
|
rect rgb(255, 266, 255)
|
||||||
|
|
||||||
|
Ansible ->> Gitea : setupApp(adminPassword)
|
||||||
|
activate Gitea
|
||||||
|
Note left of Gitea: docker playwright
|
||||||
|
deactivate Gitea
|
||||||
|
Gitea -->> Ansible : app(id,secret)
|
||||||
|
|
||||||
|
Ansible ->> Vault : generate vaultRootToken
|
||||||
|
Ansible ->> Vault : enable oidc auth backend with app(id,secret) <br> give admin policy to admin user
|
||||||
|
activate Vault
|
||||||
|
Note left of Vault: docker tofu(vaultRootToken)
|
||||||
|
deactivate Vault
|
||||||
|
Ansible ->> Vault: revoke vaultRootToken
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
```
|
||||||
@@ -9,11 +9,18 @@
|
|||||||
- [x] setup postgres
|
- [x] setup postgres
|
||||||
- [x] setup gitea
|
- [x] setup gitea
|
||||||
- [x] setup mail alert
|
- [x] setup mail alert
|
||||||
- [ ] [setup gitea runners, Argo CD](./03_cicd_gitea_action_argocd.md)
|
- [x] [setup gitea runners, Argo CD](./03_cicd_gitea_action_argocd.md)
|
||||||
- [x] sync git repo with github/gitlab
|
- [x] sync git repo with github/gitlab
|
||||||
- [ ] docker hub
|
- [ ] docker hub
|
||||||
- [ ] gitea packages
|
- [x] gitea packages
|
||||||
|
- [ ] devsecops tools
|
||||||
|
- [x] [hashicorp vault](./04_tool_hashicorp_vault.md)
|
||||||
|
- [ ] terrakube
|
||||||
|
- [ ] prometheus/grafana
|
||||||
|
- [ ] ansible AWX
|
||||||
- [ ] setup hello world web app
|
- [ ] setup hello world web app
|
||||||
|
- [ ] manage postgres credentials
|
||||||
|
- [ ] protect public endpoint (crowdsec)
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Reference: [Arcodange _**Factory**_ Ansible Collection](/ansible/arcodange/factory/README.md)
|
> Reference: [Arcodange _**Factory**_ Ansible Collection](/ansible/arcodange/factory/README.md)
|
||||||
|
|||||||
Reference in New Issue
Block a user