Add support for signatures
This commit is contained in:
parent
8e9c9198fe
commit
9e5e5f45f3
@ -146,4 +146,16 @@ void freePathInfo(struct PathInfo * const input)
|
||||
freeStrings(&input->sigs);
|
||||
}
|
||||
|
||||
// TODO: This can be done in Haskell using the `ed25519` package
|
||||
void signString
|
||||
( char const * const secretKey
|
||||
, char const * const message
|
||||
, struct string * const output
|
||||
)
|
||||
{
|
||||
std::string signature = SecretKey(secretKey).signDetached(message);
|
||||
|
||||
copyString(signature, output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -47,6 +47,8 @@ executable nix-serve-ng
|
||||
cxx-options: -std=c++17
|
||||
|
||||
build-depends: base < 5
|
||||
, base16
|
||||
, base32
|
||||
, bsd-sysctl
|
||||
, bytestring
|
||||
, http-types
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{ mkDerivation, base, bsd-sysctl, bytestring, http-types, lib
|
||||
, managed, megaparsec, mtl, network, nixstore, nixutil
|
||||
, optparse-applicative, vector, wai, wai-extra, warp, warp-tls
|
||||
{ mkDerivation, base, base16, base32, bsd-sysctl, bytestring
|
||||
, http-types, lib, managed, megaparsec, mtl, network, nixstore
|
||||
, nixutil, optparse-applicative, vector, wai, wai-extra, warp
|
||||
, warp-tls
|
||||
}:
|
||||
mkDerivation {
|
||||
pname = "nix-serve-ng";
|
||||
@ -9,8 +10,9 @@ mkDerivation {
|
||||
isLibrary = false;
|
||||
isExecutable = true;
|
||||
executableHaskellDepends = [
|
||||
base bsd-sysctl bytestring http-types managed megaparsec mtl
|
||||
network optparse-applicative vector wai wai-extra warp warp-tls
|
||||
base base16 base32 bsd-sysctl bytestring http-types managed
|
||||
megaparsec mtl network optparse-applicative vector wai wai-extra
|
||||
warp warp-tls
|
||||
];
|
||||
executableSystemDepends = [ nixstore nixutil ];
|
||||
description = "A drop-in replacement for nix-serve that's faster and more stable";
|
||||
|
||||
41
src/Main.hs
41
src/Main.hs
@ -18,6 +18,7 @@ import Sysctl (_SO_MAX_CONN)
|
||||
import qualified Control.Monad as Monad
|
||||
import qualified Control.Monad.Except as Except
|
||||
import qualified Data.ByteString as ByteString
|
||||
import qualified Data.ByteString.Char8 as ByteString.Char8
|
||||
import qualified Data.ByteString.Builder as Builder
|
||||
import qualified Data.ByteString.Lazy as ByteString.Lazy
|
||||
import qualified Data.Vector as Vector
|
||||
@ -32,9 +33,16 @@ import qualified Nix
|
||||
import qualified Options
|
||||
import qualified Options.Applicative as Options
|
||||
import qualified System.BSD.Sysctl as Sysctl
|
||||
import qualified System.Environment as Environment
|
||||
|
||||
makeApplication :: Integer -> ByteString -> Application
|
||||
makeApplication priority storeDirectory request respond = do
|
||||
data ApplicationOptions = ApplicationOptions
|
||||
{ priority :: Integer
|
||||
, storeDirectory :: ByteString
|
||||
, secretKey :: Maybe ByteString
|
||||
}
|
||||
|
||||
makeApplication :: ApplicationOptions -> Application
|
||||
makeApplication ApplicationOptions{..} request respond = do
|
||||
let stripStore = ByteString.stripPrefix (storeDirectory <> "/")
|
||||
|
||||
let done = Except.throwError
|
||||
@ -76,7 +84,7 @@ makeApplication priority storeDirectory request respond = do
|
||||
Just storePath -> do
|
||||
return storePath
|
||||
|
||||
PathInfo{..} <- liftIO (Nix.queryPathInfo storePath)
|
||||
pathInfo@PathInfo{..} <- liftIO (Nix.queryPathInfo storePath)
|
||||
|
||||
narHash2 <- case ByteString.stripPrefix "sha256:" narHash of
|
||||
Nothing -> do
|
||||
@ -115,6 +123,23 @@ makeApplication priority storeDirectory request respond = do
|
||||
Nothing ->
|
||||
return mempty
|
||||
|
||||
fingerprint <- case Nix.fingerprintPath storePath pathInfo of
|
||||
Nothing -> internalError "invalid NAR hash"
|
||||
Just builder -> do
|
||||
return (ByteString.Lazy.toStrict (Builder.toLazyByteString builder))
|
||||
|
||||
signatures <- case secretKey of
|
||||
Just key -> do
|
||||
signature <- liftIO (Nix.signString key fingerprint)
|
||||
|
||||
return (Vector.singleton signature)
|
||||
|
||||
Nothing -> do
|
||||
return sigs
|
||||
|
||||
let buildSignature signature =
|
||||
"Sig: " <> Builder.byteString signature <> "\n"
|
||||
|
||||
let builder =
|
||||
"StorePath: "
|
||||
<> Builder.byteString storePath
|
||||
@ -129,6 +154,7 @@ makeApplication priority storeDirectory request respond = do
|
||||
<> "\n"
|
||||
<> referencesBuilder
|
||||
<> deriverBuilder
|
||||
<> foldMap buildSignature signatures
|
||||
|
||||
let size =
|
||||
( ByteString.Lazy.toStrict
|
||||
@ -197,6 +223,9 @@ toSocket path = do
|
||||
|
||||
return socket
|
||||
|
||||
readSecretKey :: FilePath -> IO ByteString
|
||||
readSecretKey = fmap ByteString.Char8.strip . ByteString.readFile
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
options@Options{ priority, verbosity } <- do
|
||||
@ -204,13 +233,17 @@ main = do
|
||||
|
||||
storeDirectory <- Nix.getStoreDir
|
||||
|
||||
secretKeyFile <- Environment.lookupEnv "NIX_SECRET_KEY_FILE"
|
||||
|
||||
secretKey <- traverse readSecretKey secretKeyFile
|
||||
|
||||
let logger =
|
||||
case verbosity of
|
||||
Quiet -> id
|
||||
Normal -> RequestLogger.logStdout
|
||||
Verbose -> RequestLogger.logStdoutDev
|
||||
|
||||
let application = logger (makeApplication priority storeDirectory)
|
||||
let application = logger (makeApplication ApplicationOptions{..})
|
||||
|
||||
case options of
|
||||
Options{ ssl = Disabled, socket = TCP{ host, port } } -> do
|
||||
|
||||
51
src/Nix.hsc
51
src/Nix.hsc
@ -1,13 +1,17 @@
|
||||
{-# LANGUAGE BlockArguments #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||
{-# LANGUAGE MultiWayIf #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
|
||||
module Nix where
|
||||
|
||||
import Control.Applicative (empty)
|
||||
import Control.Monad.Managed (Managed)
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.ByteString.Builder (Builder)
|
||||
import Data.Vector (Vector)
|
||||
import Data.Word (Word64)
|
||||
import Foreign (Ptr, Storable(..))
|
||||
@ -16,6 +20,9 @@ import Foreign.C (CChar, CLong, CSize, CString)
|
||||
import qualified Control.Exception as Exception
|
||||
import qualified Control.Monad.Managed as Managed
|
||||
import qualified Data.ByteString as ByteString
|
||||
import qualified Data.ByteString.Base16 as Base16
|
||||
import qualified Data.ByteString.Base32 as Base32
|
||||
import qualified Data.ByteString.Builder as Builder
|
||||
import qualified Data.Vector as Vector
|
||||
import qualified Data.Vector.Storable as Vector.Storable
|
||||
import qualified Foreign
|
||||
@ -208,4 +215,48 @@ queryPathInfo storePath = do
|
||||
cPathInfo <- peek output
|
||||
fromCPathInfo cPathInfo
|
||||
|
||||
fingerprintPath :: ByteString -> PathInfo -> Maybe Builder
|
||||
fingerprintPath storePath PathInfo{ narHash, narSize, references } = do
|
||||
suffix <- ByteString.stripPrefix "sha256:" narHash
|
||||
|
||||
base32Suffix <- if
|
||||
| ByteString.length suffix == 64
|
||||
, Right digest <- Base16.decodeBase16 suffix ->
|
||||
return (Base32.encodeBase32' digest)
|
||||
| ByteString.length suffix == 52 ->
|
||||
return suffix
|
||||
| otherwise ->
|
||||
empty
|
||||
|
||||
return
|
||||
( "1;"
|
||||
<> Builder.byteString storePath
|
||||
<> ";sha256:"
|
||||
<> Builder.byteString base32Suffix
|
||||
<> ";"
|
||||
<> Builder.word64Dec narSize
|
||||
<> ";"
|
||||
<> referencesBuilder
|
||||
)
|
||||
where
|
||||
referencesBuilder =
|
||||
case Vector.uncons references of
|
||||
Nothing ->
|
||||
mempty
|
||||
Just (r0, rs) ->
|
||||
Builder.byteString r0
|
||||
<> foldMap (\r -> "," <> Builder.byteString r) rs
|
||||
|
||||
foreign import ccall "signString" signString_
|
||||
:: CString -> CString -> Ptr String_ -> IO ()
|
||||
|
||||
signString :: ByteString -> ByteString -> IO ByteString
|
||||
signString secretKey fingerprint =
|
||||
ByteString.useAsCString secretKey \cSecretKey ->
|
||||
ByteString.useAsCString fingerprint \cFingerprint ->
|
||||
Foreign.alloca \output -> do
|
||||
let open = signString_ cSecretKey cFingerprint output
|
||||
let close = freeString output
|
||||
Exception.bracket_ open close do
|
||||
string_ <- peek output
|
||||
fromString_ string_
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user