Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F22590118
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
47 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/GIDs b/GIDs
index 3fc4b36..7f56c1e 100644
--- a/GIDs
+++ b/GIDs
@@ -1,15 +1,16 @@
827 chaton-dev
828 deployment
829 nasqueron-irc
835 opensearch
836 opendkim
842 nasqueron-dev-docker
+1001 netbox
3001 ops
#3002 is intentionally left unassigned
3003 deployment
3004 mediawiki
3005 nasquenautes
6000 mailbox
9001 salt
9002 deploy
9003 web
diff --git a/PORTS b/PORTS
index 03aae7c..a555e28 100644
--- a/PORTS
+++ b/PORTS
@@ -1,59 +1,60 @@
devserver
2337 API api-exec HTTP
+ 17000 NetBox
webserver-alkane
9253 php-fpm metrics
reserved-for-legacy-docker-migration-medium-priority
3000 Mastodon public HTTP
4000 Mastodon streaming HTTP
15674 RabbitMQ
41080 Nasqueron Tools HTTP
reserved-for-legacy-docker-migration-low-priority
4440 Rundeck HTTP
21080 Drupal CRM HTTP
22080 Zammad HTTP
27080 Grafana HTTP
28080 phragile HTTP
29080 etcd HTTP
32080 Discourse HTTP
40080 RocketChat HTTP
paas-docker
5000 Docker registry HTTP
9090 Openfire HTTP
16080 Orbeon HTTP
17080 Penpot - back-end
17300 Penpot - exporter
19080 Nasqueron API - Datasources
20080 Nasqueron API - Docker registry API
22220 Phabricator Aphlict (client)
22221 Phabricator Aphlict (admin)
23080 Phabricator HTTP - River Sector
24080 Tommy HTTP - CI
24180 Tommy HTTP - CD
25080 Auth Grove HTTP
26080 Sentry HTTP
26300 Sentry - Relay
30080 Pixelfed HTTP
31080 Phabricator HTTP - DevCentral
33080 Bugzilla HTTP - Espace Win
34080 Etherpad
35080 Phabricator HTTP - Wolfplex
36080 Phabricator HTTP - Zed
37080 Notifications center HTTP
38080 Jenkins HTTP - CD
39080 Cachet HTTP
41080 ACME DNS server HTTP
42080 Jenkins HTTP - CI
43080 Hauk
44080 Hound
# 45080 should be reserved for OpenGrok to compare with Hound
46080 Airflow - HTTP
46555 Airflow - Flower
47080 Jenkins HTTP - Test
48080 Vault - Notifications - Integration
50000 Jenkins controller's port for JNLP-based Jenkins agents - CD
52000 Jenkins controller's port for JNLP-based Jenkins agents - Test
55000 Jenkins controller's port for JNKP-based Jenkins agents - CI
diff --git a/UIDs b/UIDs
index 005296b..fa89de9 100644
--- a/UIDs
+++ b/UIDs
@@ -1,17 +1,18 @@
830 odderon
831 builder
832 chaton LEGACY
833 viperserv
834 tc2
835 opensearch
836 opendkim
+1001 netbox
3004 mediawiki
6000 mailbox
8000 web-admin
9001 salt
9002 deploy
8900 zr LEGACY
# Web app
12000 web-org-nasqueron-mail
12001 web-org-nasqueron-mail-admin
diff --git a/pillar/credentials/vault.sls b/pillar/credentials/vault.sls
index c2844f9..1d9879d 100644
--- a/pillar/credentials/vault.sls
+++ b/pillar/credentials/vault.sls
@@ -1,294 +1,298 @@
# -------------------------------------------------------------
# Salt configuration for Nasqueron servers
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# License: Trivial work, not eligible to copyright
# -------------------------------------------------------------
# -------------------------------------------------------------
# Vault configuration
#
# :: vault_policies_path: path on vault server where to store policies
#
# :: vault_policies_source: path to fetch policies from
# if starting by salt://, from salt files server
#
# :: vault_mount_paths: translates secrets paths in policies paths
#
# Generally, Vault paths are the same for policies and data access.
#
# For kv secrets engine, version 2, writing and reading versions
# of a kv value are prefixed with the data/ path.
#
# credentials.build_policies_by_node will use this dictionary
# to be able to rewrite secrets paths in data paths.
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vault_policies_path: /srv/policies/vault
vault_policies_source: /srv/policies/vault
vault_mount_paths:
ops/secrets: ops/data/secrets
ops/privacy: ops/data/privacy
apps: apps/data
# -------------------------------------------------------------
# Vault policies to deploy as-is, i.e., without templating.
#
# Entries of vault_policies must match a .hcl file in
# the roles /vault/policies/files folder.
#
# If you need a template, create a new pillar entry instead
# and add the parsing logic either:
# - directly to roles/vault/policies/
#
# - through _modules/credentials.py for policies to apply
# to Salt nodes, like e.g., vault_secrets_by_role
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vault_policies:
- admin
- airflow
- salt-primary
- sentry
- vault_bootstrap
- viperserv
# -------------------------------------------------------------
# Vault policies for Salt itself
#
# The policy attached to the login method (e.g., approle)
# used by the Salt primary server to log in to Vault.
#
# Source is the name of a policy managed by the vault_policies
# section. Target is the name of the policy attached.
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vault_salt_primary_policy:
source: salt-primary
target: salt
# -------------------------------------------------------------
# Vault full policies to include by role
#
# Declare the extra policies each node needs.
#
# In addition to those extra policies, the vault_secrets_by_role
# will be parsed for the keys.
#
# IMPORTANT: as grains['roles'] can be modified by the node,
# roles are extracted directly from the pillar.
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vault_extra_policies_by_role:
salt-primary:
- salt-primary
# -------------------------------------------------------------
# Vault secrets by role
#
# Paths of the keys the specified role needs access to.
#
# Avoid * notation as this namespace is shared between Vault
# and the applications. As such, only secrets the Salt nodes
# need in a state they need to deploy should be listed here.
#
# Use %%node%% as variable for node name.
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vault_secrets_by_role:
devserver:
- ops/secrets/dbserver/windriver-mariadb/users/*
- ops/secrets/dbserver/windriver-pgsql/users/*
- ops/secrets/nasqueron/notifications/notifications-cli/%%node%%
- ops/secrets/nasqueron/deploy/deploy_keys/alken-orin
- ops/secrets/nasqueron/deploy/deploy_keys/by_repo/bitbucket/dereckson/www
- ops/secrets/nasqueron/deploy/deploy_keys/by_repo/bitbucket/ewosp/www
- ops/secrets/nasqueron/deploy/deploy_keys/by_repo/github/wolfplex/api-www
mailserver:
- ops/secrets/dbserver/cluster-A/users/mailManagement
- ops/secrets/dbserver/cluster-A/users/dovecot
- ops/secrets/dbserver/cluster-A/users/postfix
- ops/secrets/mailserver/security
+ netbox:
+ - ops/secrets/dbserver/windriver-pgsql/users/netbox
+ - ops/secrets/nasqueron/netbox/key
+
opensearch:
- ops/secrets/nasqueron/opensearch/infra-logs/internal_users/admin
- ops/secrets/nasqueron/opensearch/infra-logs/internal_users/dashboards
paas-docker-prod:
#
# Personal data or personally identifiable information (PII)
# related to Nasqueron Operations SIG members.
#
- ops/privacy/ops-cidr
#
# Credentials used by Nasqueron services
# Format: ops/secrets/nasqueron/service/<...>
#
- ops/secrets/nasqueron/acquisitariat/mysql
- ops/secrets/nasqueron/airflow/admin_account
- ops/secrets/nasqueron/airflow/fernet
- ops/secrets/nasqueron/airflow/sentry
- ops/secrets/dbserver/cluster-A/users/airflow
- ops/secrets/nasqueron/auth-grove/mysql
- ops/secrets/nasqueron/cachet/app_key
- ops/secrets/nasqueron/cachet/mysql
- ops/secrets/nasqueron/devcentral/mailgun
- ops/secrets/nasqueron/devcentral/mail_local
- ops/secrets/nasqueron/devcentral/mysql
- ops/secrets/nasqueron/etherpad/api
- ops/secrets/nasqueron/etherpad/mysql
- ops/secrets/nasqueron/etherpad/users/dereckson
- ops/secrets/nasqueron/notifications/broker
- ops/secrets/nasqueron/notifications/mailgun
- ops/secrets/nasqueron/notifications/sentry
- ops/secrets/nasqueron/notifications/credentials/github/nasqueron
- ops/secrets/nasqueron/notifications/credentials/github/wolfplex
- ops/secrets/nasqueron/notifications/credentials/github/keruald
- ops/secrets/nasqueron/notifications/credentials/github/trustspace
- ops/secrets/nasqueron/notifications/credentials/github/eglide
- ops/secrets/nasqueron/notifications/credentials/phabricator/nasqueron
- apps/notifications-center/dockerhub/notifications
- apps/notifications-center/dockerhub/auth-grove
- ops/secrets/nasqueron/penpot/github
- ops/secrets/nasqueron/penpot/postgresql
- ops/secrets/nasqueron/penpot/secret_key
- ops/secrets/nasqueron/pixelfed/app_key
- ops/secrets/nasqueron/pixelfed/mailgun
- ops/secrets/nasqueron/pixelfed/mysql
- ops/secrets/nasqueron/rabbitmq/white-rabbit/erlang-cookie
- ops/secrets/nasqueron/rabbitmq/white-rabbit/root
- ops/secrets/nasqueron/reports/acquisitariat
- ops/secrets/nasqueron/sentry/app_key
- ops/secrets/nasqueron/sentry/geoipupdate
- ops/secrets/nasqueron/sentry/postgresql
- ops/secrets/nasqueron/sentry/vault
#
# Credentials used by Nasqueron members private services
# Format: <username>/<service>/<type>
#
- ops/secrets/dereckson/phabricator/mysql
#
# Credentials used by projects hosted by Nasqueron
# Format: <project name>/<service>/<type>
#
- ops/secrets/dbserver/cluster-A/users/corspat
- ops/secrets/espacewin/phpbb/mysql_root
- ops/secrets/wolfplex/phabricator/mailgun
- ops/secrets/wolfplex/phabricator/mysql
- ops/secrets/zed/phabricator/mysql
- ops/secrets/zed/phabricator/sendgrid
paas-docker-dev:
#
# Credentials used by Nasqueron services
# Format: ops/secrets/nasqueron/service/<...>
#
- ops/secrets/nasqueron/airflow/admin_account
- ops/secrets/nasqueron/airflow/fernet
- ops/secrets/nasqueron/airflow/sentry
- ops/secrets/nasqueron/airflow/vault
- ops/secrets/dbserver/cluster-A/users/airflow
- ops/secrets/nasqueron/orbeon/oxf.crypto.password
- ops/secrets/nasqueron/orbeon/users/dereckson
- ops/secrets/dbserver/cluster-A/users/orbeon
- ops/secrets/nasqueron/rabbitmq/orange-rabbit/erlang-cookie
- ops/secrets/nasqueron/rabbitmq/orange-rabbit/root
- ops/secrets/nasqueron/rabbitmq/orange-rabbit/notifications
- ops/secrets/nasqueron/notifications/sentry
#
# Credentials used by projects hosted by Nasqueron
# Format: <project name>/<service>/<type>
#
- ops/secrets/espacewin/bugzilla/mysql
- ops/secrets/espacewin/bugzilla/mysql_root
saas-mediawiki:
- ops/secrets/dbserver/cluster-B/users/saas-mediawiki
- ops/secrets/nasqueron/mediawiki/secret_key
saas-wordpress:
- ops/secrets/dbserver/cluster-B/users/dereckson_blog
- ops/secrets/dereckson/wordpress/secrets
viperserv:
- ops/secrets/nasqueron/viperserv/vault
webserver-alkane-prod:
- ops/secrets/dbserver/cluster-B/users/dereckson_www
- ops/secrets/dbserver/cluster-B/users/zed
- ops/secrets/nasqueron/deploy/deploy_keys/by_repo/github/hypership/content_users
- ops/secrets/zed/hypership/secret_key
#
# Wolfplex credentials
#
- ops/secrets/nasqueron/etherpad/api
webserver-alkane-dev:
- ops/secrets/dbserver/cluster-B/users/dereckson_www51
webserver-legacy:
#
# Wolfplex credentials
#
- ops/secrets/nasqueron/etherpad/api
# -------------------------------------------------------------
# Vault secrets by dbserver cluster
#
# Paths of the keys the specified role needs access to.
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vault_secrets_by_dbserver_cluster:
# Main PostgreSQL cluster
A:
- ops/secrets/dbserver/cluster-A/users/*
# Main MariaDB cluster - Alkane PaaS, ViperServ
B:
- ops/secrets/dbserver/cluster-B/users/*
diff --git a/pillar/netbox/netbox.sls b/pillar/netbox/netbox.sls
new file mode 100644
index 0000000..828db1a
--- /dev/null
+++ b/pillar/netbox/netbox.sls
@@ -0,0 +1,14 @@
+# -------------------------------------------------------------
+# Salt — NetBox configuration
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+netbox:
+ app_port: 17000
+ db:
+ host: localhost
+ name: netbox
+ credential: dbserver/windriver-pgsql/users/netbox
+ secret_key: nasqueron/netbox/key
diff --git a/pillar/nodes/nodes.sls b/pillar/nodes/nodes.sls
index 7c4f852..57d5b3f 100644
--- a/pillar/nodes/nodes.sls
+++ b/pillar/nodes/nodes.sls
@@ -1,372 +1,373 @@
# -------------------------------------------------------------
# Salt — Nodes
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Created: 2017-10-20
# License: Trivial work, not eligible to copyright
# -------------------------------------------------------------
nodes_aliases:
netmasks:
intranought: &intranought_netmask 255.255.255.240
nodes:
##
## Forest: Nasqueron
## Semantic field: https://devcentral.nasqueron.org/P27
##
cloudhugger:
forest: nasqueron-infra
hostname: cloudhugger.nasqueron.org
roles:
- opensearch
network:
ipv6_tunnel: False
canonical_public_ipv4: 188.165.200.229
interfaces:
eno1:
device: eno1
ipv4:
address: 188.165.200.229
netmask: 255.255.255.0
gateway: 188.165.200.254
ipv6:
address: fe80::ec4:7aff:fe6a:36e8
prefix: 64
gateway: fe80::ee30:91ff:fee0:df80
complector:
forest: nasqueron-infra
hostname: complector.nasqueron.org
roles:
- vault
- salt-primary
zfs:
pool: zroot
network:
ipv6_tunnel: False
interfaces:
intranought:
device: vmx0
ipv4:
address: 172.27.27.7
netmask: *intranought_netmask
gateway: 172.27.27.1
db-A-001:
forest: nasqueron-infra
hostname: db-A-001.nasqueron.drake
roles:
- dbserver-pgsql
zfs:
pool: arcology
dbserver:
cluster: A
network:
ipv6_tunnel: False
interfaces:
intranought:
device: vmx0
ipv4:
address: 172.27.27.8
netmask: *intranought_netmask
gateway: 172.27.27.1
db-B-001:
forest: nasqueron-infra
hostname: db-B-001.nasqueron.drake
roles:
- dbserver-mysql
zfs:
pool: arcology
dbserver:
cluster: B
network:
ipv6_tunnel: False
interfaces:
intranought:
device: vmx0
ipv4:
address: 172.27.27.9
netmask: *intranought_netmask
gateway: 172.27.27.1
dns-001:
forest: nasqueron-infra
hostname: dns-001.nasqueron.org
roles:
- dns
zfs:
pool: arcology
network:
interfaces:
public:
device: vmx0
ipv4:
address: 178.32.70.109
netmask: 255.255.255.255
ipv6:
address: 2001:41d0:303:d971::1057:da7a
gateway: 2001:41d0:303:d9ff:ff:ff:ff:ff
prefix: 56
flags:
- hello_ipv6_ovh
intranought:
device: vmx1
ipv4:
address: 172.27.27.2
netmask: *intranought_netmask
gateway: 172.27.27.1
dwellers:
forest: nasqueron-dev-docker
hostname: dwellers.nasqueron.org
roles:
- paas-lxc
- paas-docker
- paas-docker-dev
- mastodon
flags:
install_docker_devel_tools: True
network:
ipv6_tunnel: True
canonical_public_ipv4: 51.255.124.11
interfaces:
public:
device: ens192
uuid: 6e05ebea-f2fd-4ca1-a21f-78a778664d8c
ipv4:
address: 51.255.124.11
netmask: *intranought_netmask
gateway: 51.210.99.254
intranought:
device: ens224
uuid: 8e8ca793-b2eb-46d8-9266-125aba6d06c4
ipv4:
address: 172.27.27.4
netmask: *intranought_netmask
gateway: 172.27.27.1
docker-002:
forest: nasqueron-infra
hostname: docker-002.nasqueron.org
roles:
- paas-docker
- paas-docker-prod
network:
ipv6_tunnel: True
canonical_public_ipv4: 51.255.124.9
interfaces:
public:
device: ens192
uuid: d55e0fec-f90b-3014-a458-9067ff8f2520
ipv4:
address: 51.255.124.10
netmask: *intranought_netmask
gateway: 51.210.99.254
intranought:
device: ens224
uuid: 57c04bcc-929b-3177-a2e3-88f84f210721
ipv4:
address: 172.27.27.5
netmask: *intranought_netmask
gateway: 172.27.27.1
hervil:
forest: nasqueron-infra
hostname: hervil.nasqueron.drake
network:
interfaces:
vmx0:
device: vmx0
ipv4:
address: 172.27.27.3
netmask: *intranought_netmask
gateway: 172.27.27.1
vmx1:
device: vmx1
ipv4:
address: 178.32.70.108
netmask: 255.255.255.255
roles:
- mailserver
- webserver-core
- webserver-alkane
router-001:
forest: nasqueron-infra
hostname: router-001.nasqueron.org
roles:
- router
network:
ipv6_tunnel: False
canonical_public_ipv4: 51.255.124.8
interfaces:
public:
device: vmx0
ipv4:
address: 51.255.124.8
netmask: *intranought_netmask
gateway: 51.210.99.254
ipv6:
address: 2001:41d0:303:d971::6a7e
gateway: 2001:41d0:303:d9ff:ff:ff:ff:ff
prefix: 64
flags:
- ipv4_ovh_failover
intranought:
device: vmx1
ipv4:
address: 172.27.27.1
netmask: *intranought_netmask
web-001:
forest: nasqueron-infra
hostname: web-001.nasqueron.org
roles:
- webserver-alkane
- webserver-alkane-prod
- saas-mediawiki
- saas-wordpress
network:
ipv6_tunnel: False
canonical_public_ipv4: 51.255.124.10
interfaces:
intranought:
device: vmx0
ipv4:
address: 172.27.27.10
netmask: *intranought_netmask
gateway: 172.27.27.1
public:
device: vmx1
ipv4:
address: 51.255.124.10
netmask: 255.255.255.255
gateway: 51.210.99.254
ipv6:
address: 2001:41d0:303:d971::517e:c0de
gateway: 2001:41d0:303:d9ff:ff:ff:ff:ff
prefix: 56
flags:
- ipv4_ovh_failover
- hello_ipv6_ovh
ysul:
forest: nasqueron-dev
hostname: ysul.nasqueron.org
roles:
- devserver
- dbserver-mysql
- viperserv
- webserver-legacy
zfs:
pool: arcology
network:
ipv6_tunnel: True
ipv6_gateway: 2001:470:1f12:9e1::1
canonical_public_ipv4: 212.83.187.132
interfaces:
igb0:
device: igb0
ipv4:
address: 163.172.49.16
netmask: 255.255.255.0
gateway: 163.172.49.1
aliases:
- 212.83.187.132
windriver:
forest: nasqueron-dev
hostname: windriver.nasqueron.org
roles:
- builder
- devserver
- dbserver-mysql
- dbserver-pgsql
- dns
- grafana
- netbox
- prometheus
- redis
- saas-nextcloud
+ - netbox
- webserver-alkane
- webserver-alkane-dev
zfs:
pool: arcology
network:
ipv6_tunnel: False
canonical_public_ipv4: 195.154.30.15
interfaces:
private_network:
device: ix0
ipv4:
address: 10.91.207.15
netmask: 255.255.255.0
igb0:
device: igb0
ipv4:
address: 195.154.30.15
netmask: 255.255.255.0
gateway: 195.154.30.1
ipv6:
address: 2001:bc8:2e84:700::da7a:7001
gateway: fe80::a293:51ff:feb7:55ef
prefix: 56
flags:
- ipv6_dhcp_duid
##
## Forest: Eglide
## Semantic field: ? (P27 used for "Eglide" too)
##
## This forest is intended to separate credentials
## between Eglide and Nasqueron servers.
##
eglide:
forest: eglide
hostname: eglide.org
roles:
- shellserver
network:
ipv6_tunnel: True
canonical_public_ipv4: 51.159.150.221
interfaces:
ens2:
device: ens2
ipv4:
address: 51.159.150.221
gateway: ""
flags:
# This interface is configured by cloud-init
- skip_interface_configuration
fixes:
rsyslog_xconsole: True
diff --git a/pillar/top.sls b/pillar/top.sls
index 75a6933..c5f518d 100644
--- a/pillar/top.sls
+++ b/pillar/top.sls
@@ -1,77 +1,78 @@
# -------------------------------------------------------------
# Salt configuration for Nasqueron servers
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Created: 2016-04-10
# License: Trivial work, not eligible to copyright
# -------------------------------------------------------------
base:
'*':
- core.users
- core.groups
- core.network
- nodes.nodes
- nodes.forests
- hotfixes.roles
- services.monitoring-reporting
- services.table
- webserver.sites
- credentials.vault
cloudhugger:
- opensearch.software
- opensearch.clusters
complector:
- credentials.vault
# To provision services
- saas.rabbitmq
docker-002:
- notifications.config
- paas.docker
- saas.jenkins
- saas.phpbb
db-A-001:
- dbserver.cluster-A
db-B-001:
- dbserver.cluster-B
dwellers:
- paas.docker
- saas.airflow
- saas.jenkins
eglide:
- shellserver.quassel
hervil:
- mailserver.vimbadmin
- mailserver.dovecot
- mailserver.postfix
ysul:
- devserver.repos
- saas.mediawiki
- viperserv.bots
- viperserv.fantoir
- webserver.labs
- webserver.wwwroot51
web-001:
- saas.mediawiki
- saas.wordpress
windriver:
- devserver.datacubes
- devserver.ports
- devserver.repos
+ - netbox.netbox
- observability.prometheus
- packages.freebsd
- webserver.labs
- webserver.wwwroot51
diff --git a/roles/netbox/netbox/config.sls b/roles/netbox/netbox/config.sls
new file mode 100644
index 0000000..4214747
--- /dev/null
+++ b/roles/netbox/netbox/config.sls
@@ -0,0 +1,42 @@
+# -------------------------------------------------------------
+# NetBox
+# -------------------------------------------------------------
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+{% set db = pillar["netbox"]["db"] %}
+{% set secret_key = pillar["netbox"]["secret_key"] %}
+
+/srv/netbox/netbox/netbox/netbox/configuration.py:
+ file.managed:
+ - source: salt://roles/netbox/netbox/files/configuration.py
+ - mode: 400
+ - user: netbox
+ - group: netbox
+ - template: jinja
+ - context:
+ db:
+ name: {{ db["name"] }}
+ user: {{ salt["credentials.get_username"](db["credential"]) }}
+ password: {{ salt["credentials.get_password"](db["credential"]) }}
+ host: {{ db["host"] }}
+ secret_key: {{ salt["credentials.get_password"](secret_key) }}
+
+# -------------------------------------------------------------
+# WSGI configuration
+# -------------------------------------------------------------
+
+/srv/netbox/gunicorn.py:
+ file.managed:
+ - source: salt://roles/netbox/netbox/files/gunicorn.py
+ - mode: 644
+ - template: jinja
+ - context:
+ app_port: {{ pillar["netbox"]["app_port"] }}
+
+/var/log/netbox:
+ file.directory:
+ - user: netbox
+ - group: netbox
+ - mode: 755
diff --git a/roles/netbox/netbox/files/configuration.py b/roles/netbox/netbox/files/configuration.py
new file mode 100644
index 0000000..5ace1f9
--- /dev/null
+++ b/roles/netbox/netbox/files/configuration.py
@@ -0,0 +1,280 @@
+# -------------------------------------------------------------
+# netbox configuration
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/netbox/netbox/files/configuration.py
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+
+#########################
+# #
+# Required settings #
+# #
+#########################
+
+# This is a list of valid fully-qualified domain names (FQDNs) for the NetBox server. NetBox will not permit write
+# access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name.
+#
+# Example: ALLOWED_HOSTS = ['netbox.example.com', 'netbox.internal.local']
+ALLOWED_HOSTS = ["*"]
+
+# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters:
+# https://docs.djangoproject.com/en/stable/ref/settings/#databases
+DATABASE = {
+ "NAME": "{{ db.name }}", # Database name
+ "USER": "{{ db.user }}", # PostgreSQL username
+ "PASSWORD": "{{ db.password }}", # PostgreSQL password
+ "HOST": "{{ db.host }}", # Database server
+ "PORT": "", # Database port (leave blank for default)
+ "CONN_MAX_AGE": 300, # Max database connection age
+}
+
+# Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate
+# configuration exists for each. Full connection details are required in both sections, and it is strongly recommended
+# to use two separate database IDs.
+REDIS = {
+ "tasks": {
+ "HOST": "localhost",
+ "PORT": 6379,
+ # Comment out `HOST` and `PORT` lines and uncomment the following if using Redis Sentinel
+ # 'SENTINELS': [('mysentinel.redis.example.com', 6379)],
+ # 'SENTINEL_SERVICE': 'netbox',
+ "USERNAME": "",
+ "PASSWORD": "",
+ "DATABASE": 0,
+ "SSL": False,
+ # Set this to True to skip TLS certificate verification
+ # This can expose the connection to attacks, be careful
+ # 'INSECURE_SKIP_TLS_VERIFY': False,
+ # Set a path to a certificate authority, typically used with a self signed certificate.
+ # 'CA_CERT_PATH': '/etc/ssl/certs/ca.crt',
+ },
+ "caching": {
+ "HOST": "localhost",
+ "PORT": 6379,
+ # Comment out `HOST` and `PORT` lines and uncomment the following if using Redis Sentinel
+ # 'SENTINELS': [('mysentinel.redis.example.com', 6379)],
+ # 'SENTINEL_SERVICE': 'netbox',
+ "USERNAME": "",
+ "PASSWORD": "",
+ "DATABASE": 1,
+ "SSL": False,
+ # Set this to True to skip TLS certificate verification
+ # This can expose the connection to attacks, be careful
+ # 'INSECURE_SKIP_TLS_VERIFY': False,
+ # Set a path to a certificate authority, typically used with a self signed certificate.
+ # 'CA_CERT_PATH': '/etc/ssl/certs/ca.crt',
+ },
+}
+
+# This key is used for secure generation of random numbers and strings. It must never be exposed outside of this file.
+# For optimal security, SECRET_KEY should be at least 50 characters in length and contain a mix of letters, numbers, and
+# symbols. NetBox will not run without this defined. For more information, see
+# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECRET_KEY
+SECRET_KEY = "{{ secret_key }}"
+
+
+#########################
+# #
+# Optional settings #
+# #
+#########################
+
+# Specify one or more name and email address tuples representing NetBox administrators. These people will be notified of
+# application errors (assuming correct email settings are provided).
+ADMINS = [
+ # ('John Doe', 'jdoe@example.com'),
+]
+
+# Permit the retrieval of API tokens after their creation.
+ALLOW_TOKEN_RETRIEVAL = False
+
+# Enable any desired validators for local account passwords below. For a list of included validators, please see the
+# Django documentation at https://docs.djangoproject.com/en/stable/topics/auth/passwords/#password-validation.
+AUTH_PASSWORD_VALIDATORS = [
+ # {
+ # 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ # 'OPTIONS': {
+ # 'min_length': 10,
+ # }
+ # },
+]
+
+# Base URL path if accessing NetBox within a directory. For example, if installed at https://example.com/netbox/, set:
+# BASE_PATH = 'netbox/'
+BASE_PATH = ""
+
+# API Cross-Origin Resource Sharing (CORS) settings. If CORS_ORIGIN_ALLOW_ALL is set to True, all origins will be
+# allowed. Otherwise, define a list of allowed origins using either CORS_ORIGIN_WHITELIST or
+# CORS_ORIGIN_REGEX_WHITELIST. For more information, see https://github.com/ottoyiu/django-cors-headers
+CORS_ORIGIN_ALLOW_ALL = False
+CORS_ORIGIN_WHITELIST = [
+ # 'https://hostname.example.com',
+]
+CORS_ORIGIN_REGEX_WHITELIST = [
+ # r'^(https?://)?(\w+\.)?example\.com$',
+]
+
+# Set to True to enable server debugging. WARNING: Debugging introduces a substantial performance penalty and may reveal
+# sensitive information about your installation. Only enable debugging while performing testing. Never enable debugging
+# on a production system.
+DEBUG = False
+
+# Set the default preferred language/locale
+DEFAULT_LANGUAGE = "en-us"
+
+# Email settings
+EMAIL = {
+ "SERVER": "localhost",
+ "PORT": 25,
+ "USERNAME": "",
+ "PASSWORD": "",
+ "USE_SSL": False,
+ "USE_TLS": False,
+ "TIMEOUT": 10, # seconds
+ "FROM_EMAIL": "",
+}
+
+# Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and
+# by anonymous users. List models in the form `<app>.<model>`. Add '*' to this list to exempt all models.
+EXEMPT_VIEW_PERMISSIONS = [
+ # 'dcim.site',
+ # 'dcim.region',
+ # 'ipam.prefix',
+]
+
+# HTTP proxies NetBox should use when sending outbound HTTP requests (e.g. for webhooks).
+# HTTP_PROXIES = {
+# 'http': 'http://10.10.1.10:3128',
+# 'https': 'http://10.10.1.10:1080',
+# }
+
+# IP addresses recognized as internal to the system. The debugging toolbar will be available only to clients accessing
+# NetBox from an internal IP.
+INTERNAL_IPS = ("127.0.0.1", "::1")
+
+# Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs:
+# https://docs.djangoproject.com/en/stable/topics/logging/
+LOGGING = {
+ "version": 1,
+ "disable_existing_loggers": False,
+ "handlers": {
+ "file": {
+ "level": "INFO",
+ "class": "logging.handlers.WatchedFileHandler",
+ "filename": "/var/log/netbox/netbox.log",
+ },
+ },
+ "loggers": {
+ "django": {
+ "handlers": ["file"],
+ "level": "INFO",
+ },
+ "netbox": {
+ "handlers": ["file"],
+ "level": "INFO",
+ },
+ },
+}
+
+# Automatically reset the lifetime of a valid session upon each authenticated request. Enables users to remain
+# authenticated to NetBox indefinitely.
+LOGIN_PERSISTENCE = False
+
+# Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users
+# are permitted to access most data in NetBox but not make any changes.
+LOGIN_REQUIRED = False
+
+# The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to
+# re-authenticate. (Default: 1209600 [14 days])
+LOGIN_TIMEOUT = None
+
+# The view name or URL to which users are redirected after logging out.
+LOGOUT_REDIRECT_URL = "home"
+
+# The file path where uploaded media such as image attachments are stored. A trailing slash is not needed. Note that
+# the default value of this setting is derived from the installed location.
+# MEDIA_ROOT = '/opt/netbox/netbox/media'
+
+# By default uploaded media is stored on the local filesystem. Using Django-storages is also supported. Provide the
+# class path of the storage driver in STORAGE_BACKEND and any configuration options in STORAGE_CONFIG. For example:
+# STORAGE_BACKEND = 'storages.backends.s3boto3.S3Boto3Storage'
+# STORAGE_CONFIG = {
+# 'AWS_ACCESS_KEY_ID': 'Key ID',
+# 'AWS_SECRET_ACCESS_KEY': 'Secret',
+# 'AWS_STORAGE_BUCKET_NAME': 'netbox',
+# 'AWS_S3_REGION_NAME': 'eu-west-1',
+# }
+
+# Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics'
+METRICS_ENABLED = True
+
+# Enable installed plugins. Add the name of each plugin to the list.
+PLUGINS = []
+
+# Plugins configuration settings. These settings are used by various plugins that the user may have installed.
+# Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings.
+# PLUGINS_CONFIG = {
+# 'my_plugin': {
+# 'foo': 'bar',
+# 'buzz': 'bazz'
+# }
+# }
+
+# Remote authentication support
+REMOTE_AUTH_ENABLED = False
+REMOTE_AUTH_BACKEND = "netbox.authentication.RemoteUserBackend"
+REMOTE_AUTH_HEADER = "HTTP_REMOTE_USER"
+REMOTE_AUTH_AUTO_CREATE_USER = True
+REMOTE_AUTH_DEFAULT_GROUPS = []
+REMOTE_AUTH_DEFAULT_PERMISSIONS = {}
+
+# This repository is used to check whether there is a new release of NetBox available. Set to None to disable the
+# version check or use the URL below to check for release in the official NetBox repository.
+RELEASE_CHECK_URL = None
+# RELEASE_CHECK_URL = 'https://api.github.com/repos/netbox-community/netbox/releases'
+
+# The file path where custom reports will be stored. A trailing slash is not needed. Note that the default value of
+# this setting is derived from the installed location.
+# REPORTS_ROOT = '/opt/netbox/netbox/reports'
+
+# Maximum execution time for background tasks, in seconds.
+RQ_DEFAULT_TIMEOUT = 300
+
+# The file path where custom scripts will be stored. A trailing slash is not needed. Note that the default value of
+# this setting is derived from the installed location.
+# SCRIPTS_ROOT = '/opt/netbox/netbox/scripts'
+
+# The name to use for the csrf token cookie.
+CSRF_COOKIE_NAME = "csrftoken"
+
+# The name to use for the session cookie.
+SESSION_COOKIE_NAME = "sessionid"
+
+# By default, NetBox will store session data in the database. Alternatively, a file path can be specified here to use
+# local file storage instead. (This can be useful for enabling authentication on a standby instance with read-only
+# database access.) Note that the user as which NetBox runs must have read and write permissions to this path.
+SESSION_FILE_PATH = None
+
+# Localization
+ENABLE_LOCALIZATION = False
+
+# Time zone (default: UTC)
+TIME_ZONE = "UTC"
+
+# Date/time formatting. See the following link for supported formats:
+# https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date
+DATE_FORMAT = "N j, Y"
+SHORT_DATE_FORMAT = "Y-m-d"
+TIME_FORMAT = "g:i a"
+SHORT_TIME_FORMAT = "H:i:s"
+DATETIME_FORMAT = "N j, Y g:i a"
+SHORT_DATETIME_FORMAT = "Y-m-d H:i"
diff --git a/roles/netbox/netbox/files/gunicorn.py b/roles/netbox/netbox/files/gunicorn.py
new file mode 100644
index 0000000..b8fb3fa
--- /dev/null
+++ b/roles/netbox/netbox/files/gunicorn.py
@@ -0,0 +1,24 @@
+# -------------------------------------------------------------
+# Configure gunicorn
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+# The IP address (typically localhost) and port that the NetBox WSGI process should listen on
+bind = "127.0.0.1:{{ app_port }}"
+
+# Number of gunicorn workers to spawn. This should typically be 2n+1, where
+# n is the number of CPU cores present.
+workers = {{grains["num_cpus"] + 1}}
+
+# Number of threads per worker process
+threads = 3
+
+# Timeout (in seconds) for a request to complete
+timeout = 120
+
+# The maximum number of requests a worker can handle before being respawned
+max_requests = 5000
+max_requests_jitter = 500
diff --git a/roles/netbox/netbox/files/rc/netbox b/roles/netbox/netbox/files/rc/netbox
new file mode 100755
index 0000000..6cc9dc8
--- /dev/null
+++ b/roles/netbox/netbox/files/rc/netbox
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# PROVIDE: netbox
+# REQUIRE: DAEMON
+# KEYWORD: shutdown
+
+# -------------------------------------------------------------
+# NetBox
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/netbox/server/files/rc/netbox
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
+# to enable this service:
+#
+# netbox_enable (bool): Set it to YES to enable netbox.
+# Default is "NO".
+# netbox_user (user): Set user to run netbox.
+# Default is "netbox".
+# netbox_port (int): Set port to run netbox.
+# Default is "17000".
+
+. /etc/rc.subr
+
+name=netbox
+rcvar=netbox_enable
+
+load_rc_config $name
+
+: ${netbox_enable:="NO"}
+: ${netbox_user:="netbox"}
+: ${netbox_port:="17000"}
+
+procname=/srv/netbox/venv/bin/python3.11
+pidfile=/var/run/netbox/netbox.pid
+netbox_service="/srv/netbox/service.sh"
+command="/usr/sbin/daemon"
+command_args="-f -t ${name} /usr/bin/env APP_PORT=${netbox_port} PID_FILE=${pidfile} ${netbox_service}"
+
+netbox_precmd()
+{
+ mkdir -p /var/run/netbox
+ chown ${netbox_user} /var/run/netbox
+}
+
+start_precmd=${name}_precmd
+run_rc_command "$1"
diff --git a/roles/netbox/netbox/files/rc/netbox.rc b/roles/netbox/netbox/files/rc/netbox.rc
new file mode 100644
index 0000000..73b36cd
--- /dev/null
+++ b/roles/netbox/netbox/files/rc/netbox.rc
@@ -0,0 +1,17 @@
+# -------------------------------------------------------------
+# NetBox
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/netbox/server/files/rc/netbox.rc
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+netbox_enable="YES"
+netbox_port="{{ app_port }}"
diff --git a/roles/netbox/netbox/files/service.sh b/roles/netbox/netbox/files/service.sh
new file mode 100755
index 0000000..3881bd7
--- /dev/null
+++ b/roles/netbox/netbox/files/service.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+# -------------------------------------------------------------
+# NetBox :: Service starter
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+set -e
+
+SERVICE_ROOT=/srv/netbox
+APP_ROOT=$SERVICE_ROOT/netbox
+
+source $SERVICE_ROOT/venv/bin/activate
+cd $APP_ROOT/netbox
+gunicorn --pid $PID_FILE --pythonpath $APP_ROOT -b "127.0.0.1:$APP_PORT" --config $SERVICE_ROOT/gunicorn.py netbox.wsgi
diff --git a/roles/netbox/netbox/init.sls b/roles/netbox/netbox/init.sls
new file mode 100644
index 0000000..23b81dc
--- /dev/null
+++ b/roles/netbox/netbox/init.sls
@@ -0,0 +1,11 @@
+# -------------------------------------------------------------
+# Netbox
+# -------------------------------------------------------------
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+include:
+ - .software
+ - .config
+ - .service
diff --git a/roles/netbox/netbox/service.sls b/roles/netbox/netbox/service.sls
new file mode 100644
index 0000000..56a06fa
--- /dev/null
+++ b/roles/netbox/netbox/service.sls
@@ -0,0 +1,37 @@
+# -------------------------------------------------------------
+# Netbox
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+{% from "map.jinja" import services, dirs with context %}
+
+# -------------------------------------------------------------
+# Service wrapper
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+/srv/netbox/service.sh:
+ file.managed:
+ - source: salt://roles/netbox/netbox/files/service.sh
+ - mode: 755
+
+# -------------------------------------------------------------
+# RC service
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+{% if services["manager"] == "rc" %}
+
+{{ dirs.etc }}/rc.d/netbox:
+ file.managed:
+ - source: salt://roles/netbox/netbox/files/rc/netbox
+ - mode: 755
+ - template: jinja
+ - context:
+ app_port: {{ pillar["netbox"]["app_port"] }}
+
+/etc/rc.conf.d/netbox:
+ file.managed:
+ - source: salt://roles/netbox/netbox/files/rc/netbox.rc
+
+{% endif %}
diff --git a/roles/netbox/netbox/software.sls b/roles/netbox/netbox/software.sls
new file mode 100644
index 0000000..5e0bddf
--- /dev/null
+++ b/roles/netbox/netbox/software.sls
@@ -0,0 +1,96 @@
+# -------------------------------------------------------------
+# Netbox
+# -------------------------------------------------------------
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+# -------------------------------------------------------------
+# NetBox
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+/srv/netbox:
+ file.directory:
+ - mode: 755
+ - makedirs: True
+
+install_netbox:
+ archive.extracted:
+ - name: /srv/netbox/netbox
+ - source: https://github.com/netbox-community/netbox/archive/refs/tags/v3.7.1.tar.gz
+ - source_hash: 97ea9106b6d29e2568c4e9c395013ca015ba7521029e8c907b6aa515dd62649a
+ - enforce_toplevel: False
+ - options: --strip-components=1
+
+# -------------------------------------------------------------
+# User account
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+netbox_group:
+ group.present:
+ - name: netbox
+ - gid: 1001
+
+netbox_user:
+ user.present:
+ - name: netbox
+ - uid: 1001
+ - gid: 1001
+
+# -------------------------------------------------------------
+# Python environment
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+/srv/netbox/venv:
+ file.directory:
+ - user: netbox
+ - group: netbox
+ - mode: 755
+
+netbox_python_venv:
+ cmd.run:
+ - name: |
+ python3 -m venv /srv/netbox/venv
+ . /srv/netbox/venv/bin/activate
+ pip install psycopg-c psycopg-pool psycopg
+ pip install $(grep -v psycopg /srv/netbox/netbox/requirements.txt)
+ - creates: /srv/netbox/venv/pyvenv.cfg
+ - runas: netbox
+
+# -------------------------------------------------------------
+# Documentation
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+/srv/netbox/netbox/netbox/project-static/docs:
+ file.directory:
+ - user: netbox
+ - group: netbox
+ - mode: 755
+
+netbox_build_documentation:
+ cmd.run:
+ - name: |
+ . /srv/netbox/venv/bin/activate
+ mkdocs build
+ - creates: /srv/netbox/netbox/netbox/project-static/docs/assets
+ - runas: netbox
+ - cwd: /srv/netbox/netbox
+
+# -------------------------------------------------------------
+# Static assets
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+/srv/netbox/netbox/netbox/static:
+ file.directory:
+ - user: netbox
+ - group: netbox
+ - mode: 755
+
+netbox_build_static:
+ cmd.run:
+ - name: |
+ . /srv/netbox/venv/bin/activate
+ python3 manage.py collectstatic
+ - creates: /srv/netbox/netbox/netbox/static/netbox.js
+ - runas: netbox
+ - cwd: /srv/netbox/netbox/netbox
diff --git a/top.sls b/top.sls
index 0870018..d9afeb4 100644
--- a/top.sls
+++ b/top.sls
@@ -1,61 +1,62 @@
# -------------------------------------------------------------
# Salt configuration for Nasqueron servers
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Created: 2016-04-10
# License: Trivial work, not eligible to copyright
# -------------------------------------------------------------
base:
'*':
- roles/core
- roles/webserver-content
'local':
- roles/salt-primary
'ysul':
- roles/builder
- roles/dbserver-mysql
- roles/devserver
- roles/viperserv
- roles/webserver-core
- roles/webserver-legacy
- roles/webserver-varnish
'windriver':
- roles/builder
- roles/dbserver-mysql
- roles/dbserver-pgsql
- roles/devserver
- roles/dns
- roles/freebsd-repo # depends of devserver/datacube, builder
- roles/grafana
+ - roles/netbox
- roles/prometheus
- roles/redis
- roles/saas-nextcloud
- roles/webserver-alkane
- roles/webserver-core
'cloudhugger':
- roles/opensearch
'db-A-001':
- roles/dbserver-pgsql
'db-B-001':
- roles/dbserver-mysql
'dns-001':
- roles/dns
'docker-002':
- roles/paas-docker
'dwellers':
- roles/paas-docker/docker
- roles/paas-lxc/lxc
- roles/saas-airflow
'eglide':
- roles/webserver-core
- roles/shellserver
'hervil':
- roles/mailserver
- roles/webserver-core
- roles/webserver-alkane
'web-001':
- roles/webserver-core
- roles/webserver-alkane
- roles/saas-mediawiki
- roles/saas-wordpress
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jan 31, 17:48 (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3403883
Default Alt Text
(47 KB)
Attached To
Mode
rOPS Nasqueron Operations
Attached
Detach File
Event Timeline
Log In to Comment