Note
Stack CKAN 2.10.11 + PostgreSQL 16 + Solr 9 + Redis + NGINX, già
predisposto per le funzionalità dei cataloghi open data italiani. Pensato per essere
provato in locale e poi spostato su un dominio reale cambiando una sola
variabile (CKAN_SITE_URL). Leggere il CHANGELOG per i
passaggi più delicati legati all'harvesting dei cataloghi federati.
Warning
La serie CKAN 2.10 ha raggiunto la fine del supporto: il progetto CKAN non rilascia più aggiornamenti, nemmeno di sicurezza. Vedi la sezione Sicurezza e aggiornamenti.
- DCAT-AP_IT — estensione
ckanext-dcatapit(Geosolutions) adeguata a CKAN 2.10.10. - OAI-PMH —
ckanext-oai-pmh-server(con supporto DataCite/OpenAIRE). - Harvesting —
ckanext-harvest+ harvester CKAN, RDF e DCAT-JSON. - Linked Open Data / RDF —
ckanext-dcat(profilieuro_dcat_ap_2,it_dcat_ap) con exportcatalog.rdf/catalog.ttle/dataset/{id}.rdf. - MQA / data.europa.eu —
ckanext-dcat-ap-edp-mqa(disattivabile, vedi sotto). - Multilingua (
ckanext-multilang) e Xloader per il DataStore.
- Docker Engine + plugin Compose v2 (comando
docker compose). Guida: https://docs.docker.com/engine/install/ubuntu/ - Verifica:
docker run hello-worldedocker compose version.
Tutta la configurazione passa dal file .env. Si parte da .env.example:
tutte le variabili hanno default sensati per un test locale, e l'unico blocco
realmente da toccare è quello in cima al file (>>> DA MODIFICARE <<<).
Variabili chiave:
| Variabile | A cosa serve |
|---|---|
CKAN_SITE_URL |
URL pubblico del portale, senza slash finale. Da qui vengono derivati automaticamente ckanext.dcat.base_uri e l'URL base OAI-PMH. |
CKAN_SYSADMIN_NAME / CKAN_SYSADMIN_PASSWORD |
Utente e password del primo amministratore. |
CKAN___BEAKER__SESSION__SECRET, CKAN___API_TOKEN__JWT__*__SECRET |
Segreti di sessione e dei token API. Lasciare le righe commentate: vengono generati casuali al primo avvio e conservati nel volume ckan_storage. Se si valorizzano a mano, mai con valori noti o di esempio. |
NGINX_PORT_HOST / NGINX_SSLPORT_HOST |
Porte host per accesso non-SSL e SSL. |
GEONAMES_USERNAME |
Username GeoNames per il geocoding (default demo). |
CKAN_OAIPMH_BASE_URL |
(opzionale) forza l'URL base OAI; se vuoto usa ${CKAN_SITE_URL}/oai. |
CKANEXT__DCATAPIT__ACCESSIBILITY_STATEMENT_URL |
URL della dichiarazione di accessibilità (per le PA è obbligatoria, Linee guida AgID: quella pubblicata su form.agid.gov.it). Se valorizzato, il footer mostra il link «Dichiarazione di accessibilità»; vuoto (default) = nessun link. |
CKANEXT__DCATAPIT__FOOTER_REBUILT_BY |
Nome mostrato nel footer dopo «rebuilt by» (es. il fornitore che gestisce l'istanza). Vuoto (default) = AgID. |
CKAN_CUSTOM_CSS_DEFAULT / CKAN_CUSTOM_CSS_FORCE |
Tema predefinito accessibile (WCAG 2.1 AA) in ckan/css.example: con true (default) all'avvio viene impostato come «Custom CSS» (Admin → Config) se l'admin non ne ha uno proprio, e aggiornato alle nuove versioni finché l'admin non lo modifica (script docker-entrypoint.d/05_custom_css.sh). false non tocca mai il Custom CSS. CKAN_CUSTOM_CSS_FORCE=true lo sovrascrive col predefinito al prossimo avvio. Per tornare al predefinito basta anche svuotare il Custom CSS dall'admin e riavviare. |
Basta impostare CKAN_SITE_URL
- HTTP locale:
CKAN_SITE_URL=http://localhost:8080 - HTTPS locale:
CKAN_SITE_URL=https://localhost:8443 - IP privato:
CKAN_SITE_URL=http://192.168.1.50:8080 - Produzione:
CKAN_SITE_URL=https://dati.miocomune.it
NGINX espone entrambi gli accessi contemporaneamente:
- non-SSL →
http://<host>:${NGINX_PORT_HOST}(default8080) - SSL →
https://<host>:${NGINX_SSLPORT_HOST}(default8443, certificato self-signed generato in automatico al primo avvio).
Si usa quello che si preferisce; CKAN_SITE_URL deve coerentemente puntare
allo schema/porta scelti. In produzione, terminare il TLS con un certificato
valido (es. reverse proxy esterno o sostituendo il cert in nginx/certs).
Eseguire i comandi in ordine, aspettando il completamento di ciascuno.
-
Clonare e preparare il
.env:git clone https://github.com/piersoft/ckan-docker-ita.git cd ckan-docker-ita cp .env.example .env -
Modificare nel
.envalmenoCKAN_SITE_URLeCKAN_SYSADMIN_PASSWORD(vedi tabella sopra). -
Costruire le immagini:
docker compose build
-
Avviare lo stack:
docker compose up -d
-
Attendere che CKAN sia healthy (il primo avvio carica i vocabolari DCAT-AP_IT e può richiedere alcuni minuti):
docker compose ps
-
Creare i gruppi tematici, una sola volta (i gruppi restano nel database, anche dopo un rebuild; se rilanciato, i gruppi già presenti vengono saltati):
docker compose exec ckan bash /srv/app/setup_groups.sh
Il portale è ora raggiungibile all'indirizzo impostato in CKAN_SITE_URL.
Note
Dopo un nuovo docker compose build il passo 6 non va ripetuto: i gruppi
sono nel database e l'assegnazione dei dataset ai gruppi usa il file
patches/theme_to_group.ini incluso nell'immagine.
-
Build (ricostruire le immagini):
docker compose build
-
Start / Stop:
docker compose up -d docker compose down # ferma e rimuove i container (i volumi restano) -
CKAN in stato
unhealthy— riavviare CKAN, attendere 2-3 minuti, poi riavviare NGINX:docker compose restart ckan sleep 180 docker compose restart nginx
-
Setup iniziale dei gruppi (vedi passo 6):
docker compose exec ckan bash /srv/app/setup_groups.sh -
Log in tempo reale:
docker compose logs -f ckan
-
Reset totale (cancella anche i dati: DB, indice Solr, storage):
docker compose down -v
-
Fine del supporto di CKAN 2.10. Le vulnerabilità di CKAN scoperte dopo la 2.10.11 non verranno corrette in questa serie. Tenerne conto nel valutare l'esposizione del portale e pianificare il passaggio a una versione supportata.
-
Aggiornare periodicamente le immagini. Gli aggiornamenti di sicurezza del sistema operativo e delle librerie entrano solo ricostruendo le immagini con le basi aggiornate (consigliato almeno una volta al mese e dopo ogni aggiornamento del repository):
git pull docker compose build --pull docker compose up -d
docker compose up -dricrea solo i container cambiati; dati, database, indice Solr e file caricati restano nei volumi. -
Esporre solo NGINX. Solr, Redis e PostgreSQL stanno su reti Docker interne e non vanno pubblicati sull'host (non aggiungere
ports:a quei servizi). In produzione usare HTTPS con un certificato valido. -
Segreti. Non impostare nel
.envvalori noti o di esempio per i segreti di sessione e dei token API: lasciando le righe commentate vengono generati al primo avvio (vedi la tabella della configurazione). Il file.envcontiene password e segreti: renderlo leggibile solo dall'amministratore (chmod 600 .env). -
Password dell'amministratore. Cambiare
CKAN_SYSADMIN_PASSWORDprima del primo avvio; in seguito la password si cambia dal profilo utente.
- Catalogo RDF:
${CKAN_SITE_URL}/catalog.rdfe.../catalog.ttl - RDF del singolo dataset:
${CKAN_SITE_URL}/dataset/{id}.rdf - OAI-PMH:
${CKAN_SITE_URL}/oai?verb=Identify,.../oai?verb=ListMetadataFormats,.../oai?verb=ListRecords&metadataPrefix=oai_dc - Harvesting: interfaccia in
${CKAN_SITE_URL}/harvest
tools/validate_edp_shacl.py valida il catalog.ttl con le stesse shape che
usa data.europa.eu, cosi' da vedere violazioni e warning prima che compaiano
sul portale europeo. Non usa le shape SEMIC "pure": scarica
shapes.ttl + deprecateduris.ttl dal progetto
piveau-metrics-validating-shacl,
cioe' il profilo "DCAT-AP 3.0.1 Level 1" con cui EDP valida i metadati
harvestati. Piveau aggiunge regole proprie che nelle shape SEMIC non esistono.
pip install rdflib pyshacl
# un singolo dataset
python3 tools/validate_edp_shacl.py https://dati.gov.it/opendata --name NOME-DATASET
# le prime 3 pagine del catalogo (100 dataset per pagina)
python3 tools/validate_edp_shacl.py https://dati.gov.it/opendata --pages 3
# tutto il catalogo: legge tutte le pagine fino all'hydra:lastPage
python3 tools/validate_edp_shacl.py https://dati.comune.lecce.it --pages tutte
# solo un ente
python3 tools/validate_edp_shacl.py https://dati.gov.it/opendata --fq organization:NOME-ENTE
# un TTL gia' scaricato
python3 tools/validate_edp_shacl.py --file catalog.ttl
# aggiunge anche le shape raccomandate, piu' severe del profilo EDP
python3 tools/validate_edp_shacl.py https://dati.gov.it/opendata --level 2Altre opzioni: --dcatap per la versione delle shape (default 3.0.0, quella
indicata nei report DQV di EDP),
--examples per quanti esempi mostrare di ogni segnalazione, --json FILE per
salvare il riepilogo. Codice di uscita: 0 se non ci sono Violation, 1 se ce
ne sono, 2 in caso di errore — quindi si puo' usare in una pipeline.
Le shape vengono scaricate una volta e messe in cache nella directory
temporanea del sistema. Due avvertenze pratiche: serve rete verso gitlab.com,
e dati.gov.it va interrogato senza www, perche' il WAF blocca le
richieste che non arrivano da un browser.
Ogni pagina viene validata separatamente, come fa EDP: unire le pagine in un unico grafo duplicherebbe i nodi anonimi dei sottocataloghi e produrrebbe falsi errori di cardinalita' sul publisher.
Violation sono non conformita' da correggere. Warning no: molti vengono
dalle shape di deprecazione di piveau, che segnalano cambi di vocabolario fra
DCAT-AP 2.1 e 3.0 e in alcuni casi penalizzano proprio il vocabolario nuovo.
Prima di inseguire un warning conviene leggere la shape che lo genera nei file
in cache, perche' non sempre indica un errore nei metadati.
Con --profile lo stesso script valida anche secondo le regole italiane. Le
shape stanno in tools/shapes/ e non vengono scaricate.
| Profilo | Cosa controlla | Fonte |
|---|---|---|
edp (default) |
shape di piveau usate da data.europa.eu | piveau-metrics-validating-shacl |
dcatapit |
identificativo, titolare (dct:rightsHolder), frequenza, temi, data di modifica; soggetti con dct:identifier e foaf:name non vuoti; punto di contatto con vcard:fn e vcard:hasEmail; formato, URL di accesso e licenza della distribuzione; tipi dcatapit:* |
Linee guida AgID per i cataloghi dati |
vocabolari |
temi sui 14 valori EU data-theme, frequenza, lingua, formato, diritti di accesso, media type IANA, URI ELI, categorie HVD |
authority table EU |
hvd |
dcatap:applicableLegislation con l'ELI del Reg. UE 2023/138 e dcatap:hvdCategory su dataset, distribuzioni e servizi |
Guida operativa AgID sui dati di elevato valore v1.0 (azione #hvd14), DCAT-AP HVD 2.2.0 |
agid |
identificativo del dataset codiceIPA:identificativo (dopo i due punti solo lettere, cifre, ., - e _) con il prefisso uguale al codice IPA del titolare; codice IPA del titolare senza caratteri speciali e presente nell'elenco enti di IndicePA (verifica in linea, --no-ipa per saltarla); email dell'editore del catalogo e dei sottocataloghi non PEC; tema o formato OP_DATPRO («dati provvisori», segnaposto) e formato ARC (quasi sempre un errore), contati nel riepilogo |
Indicazioni operative AgID per l'harvesting su dati.gov.it (non regole DCAT): escono come Info e non cambiano l'esito |
Nel profilo dcatapit le proprieta' obbligatorie producono una Violation, le
raccomandate un Warning. Il profilo hvd si applica solo alle risorse che
dichiarano l'ELI 2023/138 o una categoria HVD, alle loro distribuzioni e ai
servizi che le servono: le shape HVD ufficiali di SEMIC assumono invece un
catalogo interamente HVD. Per i vocabolari i temi sono controllati sull'elenco
chiuso dei 14 valori, gli altri valori sul namespace dell'URI; formati e media
type sugli elenchi chiusi di data.europa.eu, gli stessi usati dall'MQA.
Gli elenchi di formati (vocabolario EU file-type) e media type (registro
IANA) stanno in tools/shapes/vocabolari-elenchi.ttl, generato da
tools/aggiorna_vocabolari.py a partire dalle API
https://data.europa.eu/api/hub/search/vocabularies/{file-type,iana-media-types}.
Non si aggiorna da solo: si lancia a mano e poi si ricostruisce la pagina web.
python3 tools/aggiorna_vocabolari.py # riscrive tools/shapes/vocabolari-elenchi.ttl
cd tools/validatore-web && npm run build # per il validatore webI media type si confrontano su tipo/sottotipo, senza distinguere http/https,
www e maiuscole (EDP accetta anche gli https://iana.org/... di CKAN); i
formati sull'URI esatto.
Il profilo agid raccoglie criteri di gestione del catalogo nazionale, non
regole DCAT: identificativi univoci e riconducibili all'ente, e un'email
ordinaria (PEO) per contattare chi gestisce il catalogo. Non essendo regole,
tutte le segnalazioni sono Info ("indicazioni", in viola nella pagina) e non
rendono il catalogo non conforme. La PEC si riconosce in modo euristico (pec
come parte dell'indirizzo, cert., gestori PEC noti). Non e' incluso in it;
lo e' in tutti.
Il codice IPA del titolare (dct:rightsHolder/dct:identifier) deve comparire,
scritto esattamente allo stesso modo (maiuscole comprese: UNITO, non unito),
nel dataset "enti" di IndicePA
(https://indicepa.gov.it/ipa-dati, API datastore_search_sql); il vecchio
dataset "amministrazioni" e' nel formato precedente al 2021 e IndicePA stesso
rimanda a "enti". A IndicePA si inviano solo i codici, gia' filtrati sul
formato. Se il codice non e' di un ente ma di una sua unita' organizzativa, o
e' un codice fiscale, la segnalazione indica il codice IPA da usare. Se
IndicePA non risponde la verifica viene saltata e lo si dice nel risultato.
Prima dell'harvesting va sempre verificato che l'endpoint del catalogo e, a
campione, le risorse rispondano 200 a una richiesta HEAD (curl -I <url>),
senza WAF o filtri su IP e User-Agent: i dati aperti devono essere accessibili
e interoperabili. Il validatore non lo controlla: il risultato dipende da chi fa
la richiesta, e quello che conta e' l'harvester.
# EDP + tutti i profili italiani + regole operative AgID
python3 tools/validate_edp_shacl.py --file catalog.ttl --profile tutti
# solo i profili italiani (it = dcatapit,vocabolari,hvd)
python3 tools/validate_edp_shacl.py https://dati.gov.it/opendata --pages 2 --profile it--file accetta anche RDF/XML e JSON-LD.
tools/report_settimanale.py valida ogni settimana i cataloghi di tutte le PA presenti su dati.gov.it, con gli stessi profili del validatore web, e scrive una cartella con un report per PA:
validazione/
├── index.html pagina generale, ordinata per gravita'
├── riepilogo.csv una riga per PA
└── <slug>/
├── index.html report della PA
└── <slug>.csv elenco puntuale delle segnalazioni
- Per le PA con una harvest source DCAT-RDF valida il catalogo sorgente: la pagina 1, oppure, oltre 2.000 dataset, un campione di 6 pagine distribuite (prima, seconda, meta', ultima e alcune a caso, con un seme che cambia ogni settimana). Per le PA senza sorgente RDF (dataset inseriti dal front end, sorgenti CKAN o JSON) valida il catalogo esposto da dati.gov.it per l'organizzazione.
- Controlla anche che l'endpoint del catalogo e tre risorse a caso rispondano 200 (HEAD, oppure GET con Range se HEAD non e' ammesso).
- I codici IPA dei titolari di tutte le PA vengono verificati su IndicePA con un'unica serie di query.
- L'avviso noto su
adms:status(StatusRestrictionADMS) non entra nei CSV: viene solo contato. - Con
--telegram FILE(JSON contokenechat_id) invia un riepilogo e ilriepilogo.csva un bot Telegram.run_validazione.shlo cerca comereport-validazione.jsonnella propria cartella; va tenuto con permessi 600 e fuori dalla web root.
Per un server senza librerie Python installabili, tools/build_report_pyz.sh crea tools/dist/report_settimanale.pyz, un unico file con lo script, le regole e le librerie (rdflib, pyshacl e dipendenze, tutte in puro Python): basta Python 3.9 o successivo.
tools/build_report_pyz.sh
python3 report_settimanale.pyz --out validazione --limit 10 # prova sulle prime 10 PA
python3 report_settimanale.pyz --out validazione --org regione-toscanatools/script_validazione/run_validazione.sh e' lo script da mettere in cron: scrive in una cartella temporanea e la sostituisce solo a esecuzione riuscita, cosi' durante il lavoro resta visibile il report precedente. La cartella di uscita (OUT, obbligatoria) e l'indirizzo pubblico del report (URL, facoltativo) si scrivono in run_validazione.conf accanto allo script, che resta fuori dalla repo (.gitignore) come il file Telegram.
tools/validatore-web/ contiene la stessa validazione come pagina web: si
carica un file (Turtle, RDF/XML, N-Triples, JSON-LD), si scelgono i profili e
si ottiene l'elenco delle segnalazioni raggruppate per regola, con esempi ed
esportazione in JSON o CSV. Tutto avviene nel browser, il file non viene inviato
a nessun server. Nella scheda CKAN l'opzione «Tutte le pagine del catalogo»
scarica e valida una dopo l'altra tutte le pagine del catalog.ttl (si puo'
interrompere a meta') e restituisce un unico report. Le shape EDP sono incluse nella pagina (licenza Apache 2.0,
Fraunhofer FOKUS) invece di essere scaricate.
Il manuale d'uso e di interpretazione dei risultati (bozza, in attesa di approvazione), con l'elenco dei messaggi e
il modo di correggerli, e' in tools/docs/Manuale_Validatore_DCAT-AP_IT.pdf.
La pagina e' pubblicata su GitHub Pages dal workflow
.github/workflows/validatore-pages.yml, che riparte a ogni modifica della
pagina o delle shape. Per costruirla in locale:
cd tools/validatore-web
npm ci
npm run build # produce dist/index.html, un file unico
node test/run.mjs catalog.ttl # stessa validazione da riga di comandoLa scheda "CKAN" scarica direttamente catalog.ttl da un portale, ma funziona
solo se il server permette la lettura da altre pagine (CORS). Con i portali che
non lo permettono, dati.gov.it compreso, si scarica il file e lo si carica
dalla scheda "File".
Sugli stessi file la pagina e lo script Python danno gli stessi conteggi, per profilo e per gravita'.
Le patch nei file processors.py, rdf.py, profiles.py (dentro ckan/patches
e nelle estensioni ckanext-dcat / ckanext-dcatapit incluse) nascono
dall'analisi degli harvesting dei cataloghi nazionali, regionali e comunali su
dati.gov.it. Servono a "neutralizzare" metadati
incompleti o non conformi dei cataloghi remoti, così che l'export finale in
Linked Open Data resti corretto. Per un'installazione stand-alone (senza
harvesting di cataloghi terzi) la maggior parte di queste patch non incide.
Dettagli ed esempi nel CHANGELOG.
Se non serve l'integrazione con OpenAIRE/data.europa.eu:
- rimuovere
dcat_ap_edp_mqadalla variabileCKAN__PLUGINSnel.env; - rimuovere
dcat_ap_edp_mqadackanext.dcat.rdf.profilesinckan/docker-entrypoint.d/02_ckan-init.sh.
-
Estensione originale DCAT-AP_IT re-ingegnerizzata e aggiornata: Geosolutions — ckanext-dcatapit
-
Immagini base CKAN: ckan/ckan-docker-base
-
Progetto e personalizzazione a cura di @piersoft (Francesco Piero Paolicelli) per conto di AgID.