p2phttp: Added --socket option

Used protectedOutput to set up a umask that makes the socket only
accessible by the current user.

Authentication is still needed when using this option unless it is combined
with --wideopen. It was just simpler to keep authentication separate from
this.
This commit is contained in:
Joey Hess 2025-07-07 16:40:02 -04:00
parent 66b009a0f6
commit 492c484a82
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
6 changed files with 57 additions and 5 deletions

View file

@ -1,6 +1,7 @@
git-annex (10.20250631) UNRELEASED; urgency=medium git-annex (10.20250631) UNRELEASED; urgency=medium
* p2phttp: Scan multilevel directories with --directory. * p2phttp: Scan multilevel directories with --directory.
* p2phttp: Added --socket option.
-- Joey Hess <id@joeyh.name> Mon, 07 Jul 2025 15:59:42 -0400 -- Joey Hess <id@joeyh.name> Mon, 07 Jul 2025 15:59:42 -0400

View file

@ -22,11 +22,13 @@ import qualified Git.Construct
import qualified Annex import qualified Annex
import Types.Concurrency import Types.Concurrency
import qualified Utility.RawFilePath as R import qualified Utility.RawFilePath as R
import Utility.FileMode
import Servant import Servant
import qualified Network.Wai.Handler.Warp as Warp import qualified Network.Wai.Handler.Warp as Warp
import qualified Network.Wai.Handler.WarpTLS as Warp import qualified Network.Wai.Handler.WarpTLS as Warp
import Network.Socket (PortNumber) import Network.Socket (PortNumber)
import qualified Network.Socket as Socket
import System.PosixCompat.Files (isSymbolicLink) import System.PosixCompat.Files (isSymbolicLink)
import qualified Data.Map as M import qualified Data.Map as M
import Data.String import Data.String
@ -42,6 +44,7 @@ cmd = noMessages $ dontCheck repoExists $
data Options = Options data Options = Options
{ portOption :: Maybe PortNumber { portOption :: Maybe PortNumber
, bindOption :: Maybe String , bindOption :: Maybe String
, socketOption :: Maybe FilePath
, certFileOption :: Maybe FilePath , certFileOption :: Maybe FilePath
, privateKeyFileOption :: Maybe FilePath , privateKeyFileOption :: Maybe FilePath
, chainFileOption :: [FilePath] , chainFileOption :: [FilePath]
@ -67,6 +70,10 @@ optParser _ = Options
( long "bind" <> metavar paramAddress ( long "bind" <> metavar paramAddress
<> help "specify address to bind to" <> help "specify address to bind to"
)) ))
<*> optional (strOption
( long "socket" <> metavar paramPath
<> help "bind to unix domain socket"
))
<*> optional (strOption <*> optional (strOption
( long "certfile" <> metavar paramFile ( long "certfile" <> metavar paramFile
<> help "TLS certificate file for HTTPS" <> help "TLS certificate file for HTTPS"
@ -174,12 +181,20 @@ runServer o mst = go `finally` serverShutdownCleanup mst
let settings = Warp.setPort port $ Warp.setHost host $ let settings = Warp.setPort port $ Warp.setHost host $
Warp.defaultSettings Warp.defaultSettings
mstv <- newTMVarIO mst mstv <- newTMVarIO mst
let app = p2pHttpApp mstv
case (certFileOption o, privateKeyFileOption o) of case (certFileOption o, privateKeyFileOption o) of
(Nothing, Nothing) -> Warp.runSettings settings (p2pHttpApp mstv) (Nothing, Nothing) -> case socketOption o of
(Just certfile, Just privatekeyfile) -> do Nothing -> Warp.runSettings settings app
let tlssettings = Warp.tlsSettingsChain Just socketpath ->
certfile (chainFileOption o) privatekeyfile withsocket socketpath $ \sock ->
Warp.runTLS tlssettings settings (p2pHttpApp mstv) Warp.runSettingsSocket settings sock app
(Just certfile, Just privatekeyfile) ->
case socketOption o of
Nothing -> do
let tlssettings = Warp.tlsSettingsChain
certfile (chainFileOption o) privatekeyfile
Warp.runTLS tlssettings settings app
Just _socketpath -> giveup "HTTPS is not supported with --socket"
_ -> giveup "You must use both --certfile and --privatekeyfile options to enable HTTPS." _ -> giveup "You must use both --certfile and --privatekeyfile options to enable HTTPS."
port = maybe port = maybe
(fromIntegral defaultP2PHttpProtocolPort) (fromIntegral defaultP2PHttpProtocolPort)
@ -189,6 +204,13 @@ runServer o mst = go `finally` serverShutdownCleanup mst
(fromString "*") -- both ipv4 and ipv6 (fromString "*") -- both ipv4 and ipv6
fromString fromString
(bindOption o) (bindOption o)
withsocket socketpath =
bracket (opensocket socketpath) Socket.close
opensocket socketpath = protectedOutput $ do
sock <- Socket.socket Socket.AF_UNIX Socket.Stream 0
Socket.bind sock $ Socket.SockAddrUnix socketpath
Socket.listen sock Socket.maxListenQueue
return sock
mkServerState :: Options -> M.Map Auth P2P.ServerMode -> Annex P2PHttpServerState mkServerState :: Options -> M.Map Auth P2P.ServerMode -> Annex P2PHttpServerState
mkServerState o authenv = mkServerState o authenv =

View file

@ -103,6 +103,12 @@ convenient way to download the content of any key, by using the path
What address to bind to. The default is to bind to all addresses. What address to bind to. The default is to bind to all addresses.
* `--socket=path`
Rather than binding to an address, create and listen to a unix domain
socket at the specified location. This can be useful when proxying
to `git-annex p2phttp`.
* `--certfile=filename` * `--certfile=filename`
TLS certificate file to use. Combining this with `--privatekeyfile` TLS certificate file to use. Combining this with `--privatekeyfile`

View file

@ -1,3 +1,5 @@
For p2phttp support in forgejo-aneksajo I decided to just spawn a `git annex p2phttp --wideopen` server, do authentication on the Forgejo side, and then proxy requests to p2phttp. Since p2phttp only supports serving one repository at the moment this means that I have to allocate one free port per repository. Actually finding a free port adds complexity and a race condition, as there also seems to be no way to set `--port 0` for p2phttp and then figure out which port it bound to. For p2phttp support in forgejo-aneksajo I decided to just spawn a `git annex p2phttp --wideopen` server, do authentication on the Forgejo side, and then proxy requests to p2phttp. Since p2phttp only supports serving one repository at the moment this means that I have to allocate one free port per repository. Actually finding a free port adds complexity and a race condition, as there also seems to be no way to set `--port 0` for p2phttp and then figure out which port it bound to.
This would be simplified if p2phttp could listen on unix domain sockets instead. This would be simplified if p2phttp could listen on unix domain sockets instead.
> [[done]] --[[Joey]]

View file

@ -0,0 +1,9 @@
[[!comment format=mdwn
username="joey"
subject="""comment 3"""
date="2025-07-07T19:26:56Z"
content="""
I've made it support nested directories, which was easy.
Should be possible to make it use runSettingsSocket indeed though.
"""]]

View file

@ -0,0 +1,12 @@
[[!comment format=mdwn
username="joey"
subject="""comment 4"""
date="2025-07-07T20:28:38Z"
content="""
Implemented a --socket option. I have not tried connecting to it as a
client, but it seems to be listening to it, so I assume all is good.
Note that it still checks for authentication when using the socket,
so you will probably want to combine it with --wideopen. The socket mode
allows only the current user to access it.
"""]]