Allows you to quickly run Itko as a combined RFC6962 and static tiled CT log server.
1.1K
Current fork of Itko in: https://github.com/colin-stubbs/itkoā
You should consider volume mounts to the following paths if you desire persistence or customisation of data.
Individual files that you might want to make inject via volume mounts in order to customise Itko and disable the default configuration being applied.
Example minimal compose file for a simple default dev/test Itko instance.
services:
itko:
image: colinstubbs/itko:latest
restart: unless-stopped
environment:
# set to true to load test certs from testdata/
- LOAD_TEST_DATA=${LOAD_TEST_DATA:-false}
# set to 1+ to auto-generate certificates, NOTE: this is dependent upon LOAD_TEST_DATA being true and will not occur if LOAD_TEST_DATA is false.
- GEN_TEST_CERTS=${GEN_TEST_CERTS:-}
ports:
- "80:80"
volumes:
- data:/itko/ct
healthcheck:
test: curl --silent --fail http://localhost:80/ct/v1/get-sth | grep '"tree_size":' || exit 1
interval: 30s
timeout: 10s
retries: 3
volumes:
data:
Example container output,
user@box itko % docker compose -f ./compose.yml up
[+] Running 2/2
ā Network itko_default Created 0.0s
ā Container itko-itko-1 Created 0.0s
Attaching to itko-1
itko-1 | ### Waiting for Consul to start....OK!
itko-1 | ### Consul started, loading Itko config as KV /itko/config...
itko-1 | ### Creating 11 additional test leaf certificates...
itko-1 | ### Creating test subleaf 1 certificate...
itko-1 | ### Creating test subleaf 2 certificate...
itko-1 | ### Creating test subleaf 3 certificate...
itko-1 | ### Creating test subleaf 4 certificate...
itko-1 | ### Creating test subleaf 5 certificate...
itko-1 | ### Creating test subleaf 6 certificate...
itko-1 | ### Creating test subleaf 7 certificate...
itko-1 | ### Creating test subleaf 8 certificate...
itko-1 | ### Creating test subleaf 9 certificate...
itko-1 | ### Creating test subleaf 10 certificate...
itko-1 | ### Creating test subleaf 11 certificate...
itko-1 | ### Loading test data trusted roots...
itko-1 | ### Waiting for itko-submit to start and respond to HTTP requests....OK!
itko-1 | ### itko-submit started and responding to HTTP requests...
itko-1 | ### Loading test data...
itko-1 | ### Adding leaf cert /itko/testdata/leaf-1.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf00.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf01.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf02.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf03.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf04.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf05.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf06.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf07.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf08.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf09.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf10.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf11.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf12.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf13.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf14.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf15.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf16.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf17.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf18.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf19.chain to log...
itko-1 | ### Adding leaf cert /itko/testdata/leaf20.chain to log...
itko-1 | ### Adding 11 additional test leaf certificates...
itko-1 | ### Creating test subleaf 1 certificate chain...
itko-1 | ### Adding test subleaf 1 certificate chain...
itko-1 | ### Creating test subleaf 2 certificate chain...
itko-1 | ### Adding test subleaf 2 certificate chain...
itko-1 | ### Creating test subleaf 3 certificate chain...
itko-1 | ### Adding test subleaf 3 certificate chain...
itko-1 | ### Creating test subleaf 4 certificate chain...
itko-1 | ### Adding test subleaf 4 certificate chain...
itko-1 | ### Creating test subleaf 5 certificate chain...
itko-1 | ### Adding test subleaf 5 certificate chain...
itko-1 | ### Creating test subleaf 6 certificate chain...
itko-1 | ### Adding test subleaf 6 certificate chain...
itko-1 | ### Creating test subleaf 7 certificate chain...
itko-1 | ### Adding test subleaf 7 certificate chain...
itko-1 | ### Creating test subleaf 8 certificate chain...
itko-1 | ### Adding test subleaf 8 certificate chain...
itko-1 | ### Creating test subleaf 9 certificate chain...
itko-1 | ### Adding test subleaf 9 certificate chain...
itko-1 | ### Creating test subleaf 10 certificate chain...
itko-1 | ### Adding test subleaf 10 certificate chain...
itko-1 | ### Creating test subleaf 11 certificate chain...
itko-1 | ### Adding test subleaf 11 certificate chain...
itko-1 | ### Waiting for itko-monitor to start and get-sth to be available....OK!
itko-1 | ### itko-monitor started and responding to HTTP requests...
itko-1 | ### /ct/v1/get-sth
itko-1 | {
itko-1 | "tree_size": 35,
itko-1 | "timestamp": 1761797694013,
itko-1 | "sha256_root_hash": "tU54yoLoJjNmZCtFUEYlLsLG64Lmgns4TfKFaHqMsD4=",
itko-1 | "tree_head_signature": "BAMARzBFAiAhjfX3M9JEF/n94N8I0ftTvC9X5ts5K1wy+u3Bo2VJPAIhAKkqNd9ctqsLj+kFtH4QX3PZG/ifJ9DskNWr2YfHTaPq"
itko-1 | }
itko-1 | {"level":"info","ts":1761797694.2111993,"msg":"maxprocs: Leaving GOMAXPROCS=8: CPU quota undefined"}
itko-1 | {"level":"info","ts":1761797694.2114182,"msg":"GOMEMLIMIT is updated","package":"github.com/KimMachineGun/automemlimit/memlimit","GOMEMLIMIT":11276856115,"previous":9223372036854775807}
itko-1 | {"level":"info","ts":1761797694.2114596,"msg":"using config from file","file":"/itko/ct/Caddyfile"}
itko-1 | {"level":"info","ts":1761797694.2125597,"msg":"adapted config to JSON","adapter":"caddyfile"}
itko-1 | {"level":"info","ts":1761797694.212983,"msg":"redirected default logger","from":"stderr","to":"/var/log/caddy_access.log"}
itko-1 | {"level":"info","ts":1761797694.2241526,"msg":"serving initial configuration"}
You'll have the RFC6962 API endpoints and static tiled CT log files available via http://localhost/ā
user@box ~ % curl -v http://localhost/ct/v1/get-sth | jq
* Host localhost:80 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying [::1]:80...
* Connected to localhost (::1) port 80
> GET /ct/v1/get-sth HTTP/1.1
> Host: localhost
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Content-Length: 233
< Content-Type: application/json
< Date: Thu, 30 Oct 2025 04:16:53 GMT
< Via: 1.1 Caddy
<
{ [233 bytes data]
100 233 100 233 0 0 141k 0 --:--:-- --:--:-- --:--:-- 227k
* Connection #0 to host localhost left intact
{
"tree_size": 35,
"timestamp": 1761797813426,
"sha256_root_hash": "tU54yoLoJjNmZCtFUEYlLsLG64Lmgns4TfKFaHqMsD4=",
"tree_head_signature": "BAMARjBEAiAKfoOQOir5T7enmlAR0c7V5p2ZP0wolWMnW/EKt5kTXQIgLFsk/EDseLfNudLs/0+cefGq1D8yoxaaydHLJrfTBPc="
}
user@box ~ % curl -v http://localhost/ct/v1/get-roots | jq
* Host localhost:80 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying [::1]:80...
* Connected to localhost (::1) port 80
> GET /ct/v1/get-roots HTTP/1.1
> Host: localhost
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Thu, 30 Oct 2025 04:17:02 GMT
< Via: 1.1 Caddy
< Transfer-Encoding: chunked
<
{ [2219 bytes data]
100 2207 0 2207 0 0 591k 0 --:--:-- --:--:-- --:--:-- 718k
* Connection #0 to host localhost left intact
{
"certificates": [
"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"
]
}
user@box ~ % curl -v http://localhost/checkpoint
* Host localhost:80 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:80...
* Connected to localhost (::1) port 80
> GET /checkpoint HTTP/1.1
> Host: localhost
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 186
< Etag: "ddvd7o9whgml56"
< Last-Modified: Thu, 30 Oct 2025 04:17:24 GMT
< Server: Caddy
< Vary: Accept-Encoding
< Date: Thu, 30 Oct 2025 04:17:24 GMT
<
testlog
35
tU54yoLoJjNmZCtFUEYlLsLG64Lmgns4TfKFaHqMsD4=
ā testlog GVQfawAAAZozVayyBAMARzBFAiEA5j8gzkuDhciidXy3/s76hDbYDt7AgUZeHO56YDY09sECIHylXlgCYvFuyvFPD19uH3/9c8ldOmopcMGb2CG6BiEX
* Connection #0 to host localhost left intact
user@box ~ %
user@box ~ % curl -v -o 35 http://localhost/tile/0/000.p/35
* Host localhost:80 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying [::1]:80...
* Connected to localhost (::1) port 80
> GET /tile/0/000.p/35 HTTP/1.1
> Host: localhost
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 1120
< Etag: "ddvd5qusfxakv4"
< Last-Modified: Thu, 30 Oct 2025 04:14:53 GMT
< Server: Caddy
< Vary: Accept-Encoding
< Date: Thu, 30 Oct 2025 04:18:47 GMT
<
{ [1120 bytes data]
100 1120 100 1120 0 0 488k 0 --:--:-- --:--:-- --:--:-- 546k
* Connection #0 to host localhost left intact
user@box ~ % curl -v http://localhost/tile/0/000.p/36
* Host localhost:80 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:80...
* Connected to localhost (::1) port 80
> GET /tile/0/000.p/36 HTTP/1.1
> Host: localhost
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 404 Not Found
< Server: Caddy
< Date: Thu, 30 Oct 2025 04:18:49 GMT
< Content-Length: 0
<
* Connection #0 to host localhost left intact
user@box ~ %
monitor.json files are available via http://localhost/monitor-*.jsonā ,e.g.
user@box ~ % curl -s http://localhost/monitor-rfc6962.json | jq
{
"is_all_logs": false,
"version": "1.0.0",
"log_list_timestamp": "2025-10-30T04:14:34Z",
"name": "testing",
"operators": [
{
"name": "testing",
"email": [
"[email protected]"
],
"logs": [
{
"description": "testlog",
"log_id": "lrviNpCI/wLGL5VTfK25b8cOdbP0YA7tGoQak5jST9o=",
"key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB/hRr6qMVoOQMbeA49Ya9y82BnHs3Tu+fjZvDRwcYAt/9Z//5SRJNFbySxBfvwgf+Q7PNbWKioswClS3vx1NuQ==",
"url": "http://itko/",
"mmd": 86400,
"state": {
"usable": {
"timestamp": "2025-10-30T04:14:34Z"
}
},
"temporal_interval": {
"start_inclusive": "2000-01-01T00:00:00Z",
"end_exclusive": "2035-10-30T00:00:00Z"
}
}
],
"tiled_logs": []
}
]
}
user@box ~ % curl -s http://localhost/monitor-static.json | jq
{
"is_all_logs": false,
"version": "1.0.0",
"log_list_timestamp": "2025-10-30T04:14:34Z",
"name": "testing",
"operators": [
{
"name": "testing",
"email": [
"[email protected]"
],
"logs": [],
"tiled_logs": [
{
"description": "testlog",
"log_id": "lrviNpCI/wLGL5VTfK25b8cOdbP0YA7tGoQak5jST9o=",
"key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB/hRr6qMVoOQMbeA49Ya9y82BnHs3Tu+fjZvDRwcYAt/9Z//5SRJNFbySxBfvwgf+Q7PNbWKioswClS3vx1NuQ==",
"monitoring_url": "http://itko/",
"submission_url": "http://itko/",
"mmd": 60,
"state": {
"usable": {
"timestamp": "2025-10-30T04:14:34Z"
}
},
"temporal_interval": {
"start_inclusive": "2000-01-01T00:00:00Z",
"end_exclusive": "2035-10-30T00:00:00Z"
}
}
]
}
]
}
user@box ~ % curl -s http://localhost/monitor-combined.json | jq
{
"is_all_logs": false,
"version": "1.0.0",
"log_list_timestamp": "2025-10-30T04:14:34Z",
"name": "testing",
"operators": [
{
"name": "testing",
"email": [
"[email protected]"
],
"logs": [
{
"description": "testlog",
"log_id": "lrviNpCI/wLGL5VTfK25b8cOdbP0YA7tGoQak5jST9o=",
"key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB/hRr6qMVoOQMbeA49Ya9y82BnHs3Tu+fjZvDRwcYAt/9Z//5SRJNFbySxBfvwgf+Q7PNbWKioswClS3vx1NuQ==",
"url": "http://itko/",
"mmd": 86400,
"state": {
"usable": {
"timestamp": "2025-10-30T04:14:34Z"
}
},
"temporal_interval": {
"start_inclusive": "2000-01-01T00:00:00Z",
"end_exclusive": "2035-10-30T00:00:00Z"
}
}
],
"tiled_logs": [
{
"description": "testlog",
"log_id": "lrviNpCI/wLGL5VTfK25b8cOdbP0YA7tGoQak5jST9o=",
"key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB/hRr6qMVoOQMbeA49Ya9y82BnHs3Tu+fjZvDRwcYAt/9Z//5SRJNFbySxBfvwgf+Q7PNbWKioswClS3vx1NuQ==",
"monitoring_url": "http://itko/",
"submission_url": "http://itko/",
"mmd": 60,
"state": {
"usable": {
"timestamp": "2025-10-30T04:14:34Z"
}
},
"temporal_interval": {
"start_inclusive": "2000-01-01T00:00:00Z",
"end_exclusive": "2035-10-30T00:00:00Z"
}
}
]
}
]
}
user@box ~ %
Content type
Image
Digest
sha256:ca5bb5220ā¦
Size
119.8 MB
Last updated
8 months ago
docker pull colinstubbs/itko