commit 6ddae21727b23faa58f9ecb1a935785f612ac946
Author: Daniel Barlow <dan@telent.net>
Date: Wed, 25 Sep 2024 10:20:14 +0100
initial commit
Diffstat:
5 files changed, 138 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,7 @@
+.DELETE_ON_ERROR:
+
+default:
+
+install: default
+ mkdir -p $(TARGET)/lib/
+ cp *.fnl $(TARGET)/lib
diff --git a/README b/README
@@ -0,0 +1,26 @@
+A small HTTPS API that accepts X509 CSRs and signs them if they
+contain the magic number.
+
+Modelled on the Puppet CA "Policy-based autosigning" functionality,
+but without the rest of Puppet
+
+[ This README is speculative ]
+
+
+---
+
+DOMAIN="/C=GB/ST=London/L=London/O=Telent"
+
+openssl genrsa -out ca.key 4096
+openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -subj "${DOMAIN}/CN=CA" -out ca.crt
+
+openssl req -newkey rsa:2048 -nodes -keyout domain.key -out domain.csr -subj "${DOMAIN}/OU=devices/CN=rotuer"
+
+
+
+
+https://www.puppet.com/docs/puppet/7/ssl_attributes_extensions#csr_custom_attributes-recommended-oids-custom-attributes
+
+Custom attributes can use any public or site-specific OID, with the exception of the OIDs used for core X.509 functionality. This means you can’t re-use existing OIDs for things like subject alternative names.
+
+One useful OID is the challengePassword attribute — 1.2.840.113549.1.9.7. This is a rarely-used corner of X.509 that can easily be repurposed to hold a pre-shared key.
+\ No newline at end of file
diff --git a/default.nix b/default.nix
@@ -0,0 +1 @@
+with import <nixpkgs> {}; callPackage ./package.nix {}
diff --git a/main.fnl b/main.fnl
@@ -0,0 +1,69 @@
+(local { : view} (require :fennel))
+
+(local server (require :http.server))
+(local headers (require :http.headers))
+(local ssl (require :openssl))
+(local csr (require :openssl.x509.csr))
+(local x509 (require :openssl.x509))
+(local pkey (require :openssl.pkey))
+
+(fn not-found [out]
+ (doto (headers.new)
+ (: :append ":status" :404)
+ (: :append :content-type :text/plain)
+ (out:write_headers false))
+ (out:write_chunk "not found" true))
+
+(fn slurp [filename]
+ (with-open [f (io.open filename "r")] (f:read "*a")))
+
+(local ca-key (pkey.new (slurp "ca.key")))
+(local ca-crt (x509.new (slurp "ca.crt")))
+
+(fn new-crt [csr]
+ (let [crt
+ (doto (x509.new)
+ (: :setVersion 2)
+ (: :setSerial 42)
+ (: :setIssuer (ca-crt:getSubject))
+ (: :setLifetime (os.time) (+ (* 365 86400) (os.time)))
+ (: :setSubject (csr:getSubject))
+ (: :setPublicKey (csr:getPublicKey))
+ (: :sign ca-key))]
+ (crt:toPEM)))
+
+(fn handle-sign-csr [out]
+ (let [body (out:get_body_as_string)]
+ (doto (headers.new)
+ (: :append ":status" :200)
+ (: :append :content-type :text/plain)
+ (out:write_headers false))
+ (let [req (csr.new body)]
+ (print :subject (req:getSubject))
+ (out:write_chunk (new-crt req) true))))
+
+(fn on-stream [sv out]
+ (let [hdrs (out:get_headers)
+ method (hdrs:get ":method")
+ path (or (hdrs:get ":path") "/")]
+ (print :path path)
+ (case path
+ "/sign"
+ (handle-sign-csr out)
+ _
+ (not-found out))))
+
+
+(fn new-server []
+ (server.listen
+ {
+ :host :localhost
+ :port 8201
+ :onstream on-stream
+ }))
+
+
+(doto (new-server)
+ (: :listen)
+ (print "server ready")
+ (: :loop))
diff --git a/package.nix b/package.nix
@@ -0,0 +1,34 @@
+{
+ lua5_3
+, stdenv
+, makeWrapper
+}:
+let
+ pname = "certifix";
+ lua = lua5_3.withPackages (ps: [
+# ps.dkjson
+# ps.lpeg
+ ps.http
+ ps.luaposix
+ ]);
+ inherit makeWrapper;
+ inherit (lua.pkgs) fennel;
+in stdenv.mkDerivation {
+ inherit pname;
+ version = "0.1";
+ src = ./.;
+ makeFlags = [ "TARGET=${placeholder "out"}" ];
+ postInstall = ''
+ lua_path="`lua -e 'print(package.path)'`"
+ lua_cpath="`lua -e 'print(package.cpath)'`"
+
+ makeWrapper ${fennel}/bin/fennel \
+ $out/bin/${pname} \
+ --add-flags "--add-fennel-path $out/lib/?.fnl" \
+ --add-flags "--add-package-path '$out/lib/?.lua$lua_path'" \
+ --add-flags "--add-package-cpath '$out/lib/?.lua$lua_cpath'" \
+ --add-flags "$out/lib/main.fnl"
+ '';
+ buildInputs = [lua fennel];
+ nativeBuildInputs = [ makeWrapper ];
+}