colinstubbs/itko

By colinstubbs

•Updated 8 months ago

Allows you to quickly run Itko as a combined RFC6962 and static tiled CT log server.

Image
Security
Developer tools
Web servers
0

1.1K

colinstubbs/itko repository overview

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.

  • /itko/ct - if you want to expose/persist Itko's configuration and data.
  • /itko/ct/storage - if you just want to expose/persist Itko's data.
  • /itko/testdata - custom test data
  • /itko/testdata/generated - custom generated test certs
  • /consul/config - if you want to expose/persist Consul's configuration.
  • /consul/data - if you want to persist Consul's data.
  • /var/log - if you want to expose and/or persist log files.

Individual files that you might want to make inject via volume mounts in order to customise Itko and disable the default configuration being applied.

  • /itko/ct/config.json - JSON object that is injected to Consul as kv/${ITKO_KV_PATH}/config
  • ${ITKO_KEY_PATH} - private key for the Itko CT log, ensure you adjust ${ITKO_LOG_ID} and ${ITKO_LOG_PUBLIC_KEY} as well
  • ${ITKO_ROOT_DIRECTORY}/monitor-rfc6962.json
  • ${ITKO_ROOT_DIRECTORY}/monitor-static.json
  • ${ITKO_ROOT_DIRECTORY}/monitor-combined.json
  • /itko/ct/Caddyfile - alternatively adjust ${CADDY_CONFIG_FILE}

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 ~ % 

Tag summary

Content type

Image

Digest

sha256:ca5bb5220…

Size

119.8 MB

Last updated

8 months ago

docker pull colinstubbs/itko