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
* p2phttp: Scan multilevel directories with --directory.
* p2phttp: Added --socket option.
-- 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 Types.Concurrency
import qualified Utility.RawFilePath as R
import Utility.FileMode
import Servant
import qualified Network.Wai.Handler.Warp as Warp
import qualified Network.Wai.Handler.WarpTLS as Warp
import Network.Socket (PortNumber)
import qualified Network.Socket as Socket
import System.PosixCompat.Files (isSymbolicLink)
import qualified Data.Map as M
import Data.String
@ -42,6 +44,7 @@ cmd = noMessages $ dontCheck repoExists $
data Options = Options
{ portOption :: Maybe PortNumber
, bindOption :: Maybe String
, socketOption :: Maybe FilePath
, certFileOption :: Maybe FilePath
, privateKeyFileOption :: Maybe FilePath
, chainFileOption :: [FilePath]
@ -67,6 +70,10 @@ optParser _ = Options
( long "bind" <> metavar paramAddress
<> help "specify address to bind to"
))
<*> optional (strOption
( long "socket" <> metavar paramPath
<> help "bind to unix domain socket"
))
<*> optional (strOption
( long "certfile" <> metavar paramFile
<> 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 $
Warp.defaultSettings
mstv <- newTMVarIO mst
let app = p2pHttpApp mstv
case (certFileOption o, privateKeyFileOption o) of
(Nothing, Nothing) -> Warp.runSettings settings (p2pHttpApp mstv)
(Just certfile, Just privatekeyfile) -> do
(Nothing, Nothing) -> case socketOption o of
Nothing -> Warp.runSettings settings app
Just socketpath ->
withsocket socketpath $ \sock ->
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 (p2pHttpApp mstv)
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."
port = maybe
(fromIntegral defaultP2PHttpProtocolPort)
@ -189,6 +204,13 @@ runServer o mst = go `finally` serverShutdownCleanup mst
(fromString "*") -- both ipv4 and ipv6
fromString
(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 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.
* `--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`
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.
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.
"""]]