Compare commits

...

4 Commits

Author SHA1 Message Date
d676436e38
Fix private file path checks 2025-10-06 14:35:11 +02:00
70ea76007c
Sample package 2025-10-06 14:33:59 +02:00
2ef34fe396
Add debug output 2025-10-06 14:33:59 +02:00
9126d8d979
Do not serve derivations with /.private 2025-10-06 14:33:59 +02:00
3 changed files with 48 additions and 0 deletions

View File

@ -83,6 +83,10 @@
rec { rec {
packages = { packages = {
inherit nix-serve-ng lix-serve-ng; inherit nix-serve-ng lix-serve-ng;
private = pkgs.runCommand "private" { } ''
mkdir $out
touch $out/.private
'';
default = nix-serve-ng; default = nix-serve-ng;
}; };

View File

@ -48,6 +48,7 @@ executable nix-serve
, base32 , base32
, bytestring , bytestring
, charset , charset
, directory
, http-types , http-types
, managed , managed
, megaparsec , megaparsec

View File

@ -6,10 +6,13 @@
module Main where module Main where
import Debug.Trace
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.CharSet.ByteSet (ByteSet(..)) import Data.CharSet.ByteSet (ByteSet(..))
import Data.Function ((&)) import Data.Function ((&))
import Data.Word (Word8)
import Network.Socket (SockAddr(..)) import Network.Socket (SockAddr(..))
import Network.Wai (Application) import Network.Wai (Application)
import Nix (NoSuchPath(..), PathInfo(..)) import Nix (NoSuchPath(..), PathInfo(..))
@ -35,6 +38,7 @@ import qualified Network.Wai.Middleware.RequestLogger as RequestLogger
import qualified Nix import qualified Nix
import qualified Options import qualified Options
import qualified Options.Applicative as Options import qualified Options.Applicative as Options
import qualified System.Directory as Directory
import qualified System.Environment as Environment import qualified System.Environment as Environment
data ApplicationOptions = ApplicationOptions data ApplicationOptions = ApplicationOptions
@ -54,6 +58,20 @@ validHashPartBytes =
<> [ 0x76 .. 0x7A ] -- vwxyz <> [ 0x76 .. 0x7A ] -- vwxyz
) )
type HostAddressTuple = (Word8, Word8, Word8, Word8)
isAllowed :: Socket.HostAddress -> Bool
isAllowed host = any (uncurry (ipMatches $ Socket.hostAddressToTuple host)) allowedIPs
where
allowedIPs :: [(HostAddressTuple, HostAddressTuple)]
allowedIPs = [
((127,0,0,1), (127,0,0,1)),
((10,0,0,1), (10,255,255,254)),
((192,168,72,1), (192,168,79,254))
]
ipMatches ip a b = ip >= a && ip <= b
validHashPart :: ByteString -> Bool validHashPart :: ByteString -> Bool
validHashPart hash = ByteString.all (`ByteSet.member` validHashPartBytes) hash validHashPart hash = ByteString.all (`ByteSet.member` validHashPartBytes) hash
@ -250,6 +268,31 @@ makeApplication ApplicationOptions{..} request respond = do
done response done response
let privateFilePath = ByteString.Char8.unpack storePath ++ "/.private"
isPrivate <- liftIO $ Directory.doesPathExist privateFilePath
let sockAddr = Wai.remoteHost request
hostAddr <- case sockAddr of
SockAddrInet _ host -> return host
_ -> return $ Socket.tupleToHostAddress (255, 255, 255, 255)
traceM $ show (Socket.hostAddressToTuple hostAddr, isAllowed hostAddr)
traceM $ show (privateFilePath, isPrivate)
Monad.when (isPrivate && (not $ isAllowed hostAddr)) do
let headers = [ ("Content-Type", "text/plain") ]
let builder = "Forbidden.\n"
let response =
Wai.responseBuilder
Types.status403
headers
builder
done response
let streamingBody write flush = do let streamingBody write flush = do
result <- Nix.dumpPath hashPart callback result <- Nix.dumpPath hashPart callback