Skip to content

Commit 475df34

Browse files
committed
Add --skip-key-group-permission-check command line argument, disabling group permissions check for VRF key.
1 parent b311bbc commit 475df34

File tree

9 files changed

+80
-59
lines changed

9 files changed

+80
-59
lines changed

bench/tx-generator/src/Cardano/TxGenerator/Setup/NodeConfig.hs

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ mkNodeConfig configFp_
7474
, shelleyVRFFile = Just ""
7575
, shelleyCertFile = Just ""
7676
, shelleyBulkCredsFile = Just ""
77+
, isGroupPermissionChecked = def
7778
}
7879
, pncValidateDB = Last $ Just False
7980
, pncShutdownConfig = Last $ Just $ ShutdownConfig Nothing Nothing

cardano-node/ChangeLog.md

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
- KeepAlive client tracing: dropped `KeepAliveClient` from `kind` of
3131
`AddSample` messages in the legacy tracing system.
3232

33+
- Add `--skip-key-group-permission-check` command line argument, disabling group permissions check for VRF key.
34+
3335
## 10.2 -- January 2025
3436

3537
- Use p2p network stack by default, warn when using the legacy network stack.

cardano-node/cardano-node.cabal

+2
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ library
172172
, dns
173173
, ekg-wai
174174
, ekg-core
175+
, extra
175176
, filepath
176177
, formatting
177178
, generic-data
@@ -263,6 +264,7 @@ test-suite cardano-node-test
263264
, cardano-protocol-tpraos
264265
, cardano-node
265266
, cardano-slotting
267+
, data-default-class
266268
, directory
267269
, filepath
268270
, hedgehog

cardano-node/src/Cardano/Node/Configuration/POM.hs

+5-4
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,21 @@ import Ouroboros.Consensus.Node (NodeDatabasePaths (..))
4545
import qualified Ouroboros.Consensus.Node as Consensus (NetworkP2PMode (..))
4646
import Ouroboros.Consensus.Node.Genesis (GenesisConfig, GenesisConfigFlags,
4747
defaultGenesisConfigFlags, mkGenesisConfig)
48-
import qualified Ouroboros.Network.Diffusion.Configuration as Ouroboros
49-
import Ouroboros.Network.Mux (ForkPolicy, noBindForkPolicy, responderForkPolicy)
50-
import qualified Ouroboros.Network.PeerSelection.Governor as PeerSelection
5148
import Ouroboros.Consensus.Storage.LedgerDB.Args (QueryBatchSize (..))
5249
import Ouroboros.Consensus.Storage.LedgerDB.Snapshots (NumOfDiskSnapshots (..),
5350
SnapshotInterval (..))
5451
import Ouroboros.Consensus.Storage.LedgerDB.V1.Args (FlushFrequency (..))
5552
import Ouroboros.Network.Diffusion.Configuration as Configuration
53+
import qualified Ouroboros.Network.Diffusion.Configuration as Ouroboros
54+
import Ouroboros.Network.Mux (ForkPolicy, noBindForkPolicy, responderForkPolicy)
55+
import qualified Ouroboros.Network.PeerSelection.Governor as PeerSelection
5656

5757
import Control.Concurrent (getNumCapabilities)
5858
import Control.Monad (unless, void, when)
5959
import Data.Aeson
6060
import qualified Data.Aeson.Types as Aeson
6161
import Data.Bifunctor (Bifunctor (..))
62+
import Data.Default.Class
6263
import Data.Hashable (Hashable)
6364
import Data.Maybe
6465
import Data.Monoid (Last (..))
@@ -825,7 +826,7 @@ makeNodeConfiguration pnc = do
825826
-- they are not minting blocks.
826827
case getLast $ pncProtocolFiles pnc of
827828
Just pFiles -> pFiles
828-
Nothing -> ProtocolFilepaths Nothing Nothing Nothing Nothing Nothing Nothing
829+
Nothing -> ProtocolFilepaths Nothing Nothing Nothing Nothing Nothing Nothing def
829830
, ncValidateDB = validateDB
830831
, ncShutdownConfig = shutdownConfig
831832
, ncStartAsNonProducingNode = startAsNonProducingNode

cardano-node/src/Cardano/Node/Parsers.hs

+16
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import Cardano.Prelude (ConvertText (..))
2323
import Ouroboros.Consensus.Ledger.SupportsMempool
2424
import Ouroboros.Consensus.Node
2525

26+
import Data.Default.Class
2627
import Data.Foldable
2728
import Data.Maybe (fromMaybe)
2829
import Data.Monoid (Last (..))
@@ -31,6 +32,7 @@ import Data.Word (Word32)
3132
import Options.Applicative hiding (str)
3233
import qualified Options.Applicative as Opt
3334
import qualified Options.Applicative.Help as OptI
35+
import System.Info.Extra (isWindows)
3436
import System.Posix.Types (Fd (..))
3537
import Text.Read (readMaybe)
3638

@@ -60,6 +62,7 @@ nodeRunParser = do
6062
shelleyVRFFile <- optional parseVrfKeyFilePath
6163
shelleyCertFile <- optional parseOperationalCertFilePath
6264
shelleyBulkCredsFile <- optional parseBulkCredsFilePath
65+
isGroupPermissionChecked <- parseSkipGroupPermissionCheck
6366
startAsNonProducingNode <- lastOption parseStartAsNonProducingNode
6467

6568
-- Node Address
@@ -93,6 +96,7 @@ nodeRunParser = do
9396
, shelleyVRFFile
9497
, shelleyCertFile
9598
, shelleyBulkCredsFile
99+
, isGroupPermissionChecked
96100
}
97101
, pncValidateDB = validate
98102
, pncShutdownConfig =
@@ -349,6 +353,15 @@ parseVrfKeyFilePath =
349353
<> completer (bashCompleter "file")
350354
)
351355

356+
parseSkipGroupPermissionCheck :: Parser IsGroupPermissionChecked
357+
parseSkipGroupPermissionCheck = asum
358+
[ whenA (not isWindows) $
359+
flag def SkipFileGroupPermissionCheck $
360+
long "skip-key-group-permission-check"
361+
<> help "Skip checking of group permissions for the key files."
362+
, pure def
363+
]
364+
352365
parseStartAsNonProducingNode :: Parser Bool
353366
parseStartAsNonProducingNode =
354367
switch $ mconcat
@@ -373,3 +386,6 @@ parserHelpOptions = fromMaybe mempty . OptI.unChunk . OptI.fullDesc (Opt.prefs m
373386
renderHelpDoc :: Int -> OptI.Doc -> String
374387
renderHelpDoc cols =
375388
(`OptI.renderShowS` "") . OptI.layoutPretty (OptI.LayoutOptions (OptI.AvailablePerLine cols 1.0))
389+
390+
whenA :: Alternative f => Bool -> f a -> f a
391+
whenA cond v = if cond then v else empty

cardano-node/src/Cardano/Node/Run.hs

+31-44
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ import Ouroboros.Network.Subscription (DnsSubscriptionTarget (..),
123123

124124
import Control.Concurrent (killThread, mkWeakThreadId, myThreadId, getNumCapabilities)
125125
import Control.Concurrent.Class.MonadSTM.Strict
126-
import Control.Exception (try, IOException)
126+
import Control.Exception (try, Exception, IOException)
127127
import qualified Control.Exception as Exception
128128
import Control.Monad (forM, forM_, unless, void, when)
129129
import Control.Monad.Class.MonadThrow (MonadThrow (..))
@@ -185,34 +185,24 @@ runNode cmdPc = do
185185

186186
putStrLn $ "Node configuration: " <> show nc
187187

188-
case shelleyVRFFile $ ncProtocolFiles nc of
189-
Just vrfFp -> do vrf <- runExceptT $ checkVRFFilePermissions (File vrfFp)
190-
case vrf of
191-
Left err -> Exception.throwIO err
192-
Right () ->
193-
pure ()
194-
Nothing -> pure ()
195-
196-
eitherSomeProtocol <- runExceptT $ mkConsensusProtocol
197-
(ncProtocolConfig nc)
198-
-- TODO: Convert ncProtocolFiles to Maybe as relay nodes
199-
-- don't need these.
200-
(Just $ ncProtocolFiles nc)
201-
202-
p :: SomeConsensusProtocol <-
203-
case eitherSomeProtocol of
204-
Left err -> Exception.throwIO err
205-
Right p -> pure p
206-
207-
let networkMagic :: Api.NetworkMagic =
208-
case p of
209-
SomeConsensusProtocol _ runP ->
210-
let ProtocolInfo { pInfoConfig } = fst $ Api.protocolInfo @IO runP
211-
in getNetworkMagic $ Consensus.configBlock pInfoConfig
212-
213-
case p of
214-
SomeConsensusProtocol blockType runP ->
215-
handleNodeWithTracers cmdPc nc p networkMagic blockType runP
188+
case ncProtocolFiles nc of
189+
ProtocolFilepaths{shelleyVRFFile=Just vrfFp, isGroupPermissionChecked} ->
190+
runThrowExceptT $
191+
checkVRFFilePermissions isGroupPermissionChecked (File vrfFp)
192+
_ -> pure ()
193+
194+
consensusProtocol <-
195+
runThrowExceptT $
196+
mkConsensusProtocol
197+
(ncProtocolConfig nc)
198+
-- TODO: Convert ncProtocolFiles to Maybe as relay nodes
199+
-- don't need these.
200+
(Just $ ncProtocolFiles nc)
201+
202+
handleNodeWithTracers cmdPc nc consensusProtocol
203+
204+
runThrowExceptT :: Exception e => ExceptT e IO a -> IO a
205+
runThrowExceptT act = runExceptT act >>= either Exception.throwIO pure
216206

217207
-- | Workaround to ensure that the main thread throws an async exception on
218208
-- receiving a SIGTERM signal.
@@ -233,17 +223,13 @@ installSigTermHandler = do
233223
return ()
234224

235225
handleNodeWithTracers
236-
:: ( TraceConstraints blk
237-
, Api.Protocol IO blk
238-
)
239-
=> PartialNodeConfiguration
226+
:: PartialNodeConfiguration
240227
-> NodeConfiguration
241228
-> SomeConsensusProtocol
242-
-> Api.NetworkMagic
243-
-> Api.BlockType blk
244-
-> Api.ProtocolInfoArgs blk
245229
-> IO ()
246-
handleNodeWithTracers cmdPc nc0 p networkMagic blockType runP = do
230+
handleNodeWithTracers cmdPc nc0 p@(SomeConsensusProtocol blockType runP) = do
231+
let ProtocolInfo{pInfoConfig} = fst $ Api.protocolInfo @IO runP
232+
networkMagic :: Api.NetworkMagic = getNetworkMagic $ Consensus.configBlock pInfoConfig
247233
-- This IORef contains node kernel structure which holds node kernel.
248234
-- Used for ledger queries and peer connection status.
249235
nodeKernelData <- mkNodeKernelData
@@ -913,17 +899,18 @@ canonDbPath NodeConfiguration{ncDatabaseFile = nodeDatabaseFps} =
913899

914900
-- | Make sure the VRF private key file is readable only
915901
-- by the current process owner the node is running under.
916-
checkVRFFilePermissions :: File content direction -> ExceptT VRFPrivateKeyFilePermissionError IO ()
902+
checkVRFFilePermissions :: IsGroupPermissionChecked -> File content direction -> ExceptT VRFPrivateKeyFilePermissionError IO ()
917903
#ifdef UNIX
918-
checkVRFFilePermissions (File vrfPrivKey) = do
904+
checkVRFFilePermissions isGroupPermissionChecked (File vrfPrivKey) = do
919905
fs <- liftIO $ getFileStatus vrfPrivKey
920906
let fm = fileMode fs
921907
-- Check the the VRF private key file does not give read/write/exec permissions to others.
922-
when (hasOtherPermissions fm)
923-
(left $ OtherPermissionsExist vrfPrivKey)
908+
when (hasOtherPermissions fm) $
909+
left $ OtherPermissionsExist vrfPrivKey
924910
-- Check the the VRF private key file does not give read/write/exec permissions to any group.
925-
when (hasGroupPermissions fm)
926-
(left $ GroupPermissionsExist vrfPrivKey)
911+
when (isGroupPermissionChecked == CheckFileGroupPermission) $
912+
when (hasGroupPermissions fm) $
913+
left $ GroupPermissionsExist vrfPrivKey
927914
where
928915
hasPermission :: FileMode -> FileMode -> Bool
929916
hasPermission fModeA fModeB = fModeA `intersectFileModes` fModeB /= nullFileMode
@@ -934,7 +921,7 @@ checkVRFFilePermissions (File vrfPrivKey) = do
934921
hasGroupPermissions :: FileMode -> Bool
935922
hasGroupPermissions fm' = fm' `hasPermission` groupModes
936923
#else
937-
checkVRFFilePermissions (File vrfPrivKey) = do
924+
checkVRFFilePermissions _ (File vrfPrivKey) = do
938925
attribs <- liftIO $ getFileAttributes vrfPrivKey
939926
-- https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
940927
-- https://docs.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants

cardano-node/src/Cardano/Node/Types.hs

+11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module Cardano.Node.Types
1717
, PeerSnapshotFile (..)
1818
, CheckpointsFile(..)
1919
, ProtocolFilepaths (..)
20+
, IsGroupPermissionChecked(..)
2021
, GenesisHash(..)
2122
, CheckpointsHash(..)
2223
, MaxConcurrencyBulkSync(..)
@@ -50,6 +51,7 @@ import Ouroboros.Network.NodeToNode (DiffusionMode (..))
5051
import Control.Exception
5152
import Data.Aeson
5253
import Data.ByteString (ByteString)
54+
import Data.Default.Class (Default (..))
5355
import Data.Monoid (Last (..))
5456
import Data.String (IsString)
5557
import Data.Text (Text)
@@ -172,8 +174,17 @@ data ProtocolFilepaths =
172174
, shelleyVRFFile :: !(Maybe FilePath)
173175
, shelleyCertFile :: !(Maybe FilePath)
174176
, shelleyBulkCredsFile :: !(Maybe FilePath)
177+
, isGroupPermissionChecked :: !IsGroupPermissionChecked
175178
} deriving (Eq, Show)
176179

180+
data IsGroupPermissionChecked
181+
= CheckFileGroupPermission
182+
| SkipFileGroupPermissionCheck
183+
deriving stock (Eq, Show)
184+
185+
instance Default IsGroupPermissionChecked where
186+
def = CheckFileGroupPermission
187+
177188
newtype GenesisHash = GenesisHash (Crypto.Hash Crypto.Blake2b_256 ByteString)
178189
deriving newtype (Eq, Show, ToJSON, FromJSON)
179190

cardano-node/test/Test/Cardano/Node/FilePermissions.hs

+7-7
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ import System.IO (FilePath, IO)
3838
import Text.Show (Show (..))
3939

4040
#ifdef UNIX
41-
import Cardano.Node.Types (VRFPrivateKeyFilePermissionError (..))
41+
import Cardano.Node.Types (VRFPrivateKeyFilePermissionError (..), IsGroupPermissionChecked(..))
4242

4343
import System.Posix.Files
4444
import System.Posix.IO (closeFd, createFile)
4545
import System.Posix.Types (FileMode)
4646

4747
import Control.Exception (bracket)
48-
import Hedgehog (Gen, classify, forAll)
48+
import Hedgehog
4949
import qualified Hedgehog.Gen as Gen
5050
#endif
5151

@@ -56,10 +56,10 @@ prop_createVRFFileWithOwnerPermissions :: Property
5656
prop_createVRFFileWithOwnerPermissions =
5757
property $ do
5858
let vrfSign = "vrf-signing-key"
59-
vrfSkey <- liftIO $ generateSigningKey AsVrfKey
59+
vrfSkey <- evalIO $ generateSigningKey AsVrfKey
6060
createFileWithOwnerPermissions vrfSign vrfSkey
6161

62-
fResult <- liftIO . runExceptT $ checkVRFFilePermissions vrfSign
62+
fResult <- evalIO . runExceptT $ checkVRFFilePermissions CheckFileGroupPermission vrfSign
6363
case fResult of
6464
Left err -> failWith Nothing $ show err
6565
Right () -> liftIO (removeFile (unFile vrfSign)) >> success
@@ -84,7 +84,7 @@ prop_sanityCheck_checkVRFFilePermissions =
8484
correctResult <-
8585
liftIO $ bracket (createFile (unFile vrfPrivateKeyCorrect) correctPermission)
8686
(\h -> closeFd h >> removeFile (unFile vrfPrivateKeyCorrect))
87-
(const . liftIO . runExceptT $ checkVRFFilePermissions vrfPrivateKeyCorrect)
87+
(const . liftIO . runExceptT $ checkVRFFilePermissions CheckFileGroupPermission vrfPrivateKeyCorrect)
8888
case correctResult of
8989
Left err ->
9090
failWith Nothing $ "checkVRFFilePermissions should not have failed with error: "
@@ -105,7 +105,7 @@ prop_sanityCheck_checkVRFFilePermissions =
105105
setFileMode (unFile vrfPrivateKeyOther) $ createPermissions oPermissions
106106
return h)
107107
(\h -> closeFd h >> removeFile (unFile vrfPrivateKeyOther))
108-
(const .liftIO . runExceptT $ checkVRFFilePermissions vrfPrivateKeyOther)
108+
(const .liftIO . runExceptT $ checkVRFFilePermissions CheckFileGroupPermission vrfPrivateKeyOther)
109109
case otherResult of
110110
Left (OtherPermissionsExist _) -> success
111111
Left err ->
@@ -128,7 +128,7 @@ prop_sanityCheck_checkVRFFilePermissions =
128128
setFileMode (unFile vrfPrivateKeyGroup) $ createPermissions gPermissions
129129
return h)
130130
(\h -> closeFd h >> removeFile (unFile vrfPrivateKeyGroup))
131-
(const . liftIO . runExceptT $ checkVRFFilePermissions vrfPrivateKeyGroup)
131+
(const . liftIO . runExceptT $ checkVRFFilePermissions CheckFileGroupPermission vrfPrivateKeyGroup)
132132
case groupResult of
133133
Left (GroupPermissionsExist _) -> success
134134
Left err ->

cardano-node/test/Test/Cardano/Node/POM.hs

+5-4
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,26 @@ import Cardano.Node.Handlers.Shutdown
1515
import Cardano.Node.Types
1616
import Cardano.Tracing.Config (PartialTraceOptions (..), defaultPartialTraceConfiguration,
1717
partialTraceSelectionToEither)
18+
import Ouroboros.Cardano.Network.Diffusion.Configuration (defaultNumberOfBigLedgerPeers)
1819
import Ouroboros.Consensus.Node (NodeDatabasePaths (..))
1920
import qualified Ouroboros.Consensus.Node as Consensus (NetworkP2PMode (..))
2021
import Ouroboros.Consensus.Node.Genesis (disableGenesisConfig)
22+
import Ouroboros.Consensus.Storage.LedgerDB.Args
2123
import Ouroboros.Consensus.Storage.LedgerDB.Snapshots (NumOfDiskSnapshots (..),
2224
SnapshotInterval (..))
23-
import Ouroboros.Consensus.Storage.LedgerDB.Args
2425
import Ouroboros.Network.Block (SlotNo (..))
2526
import Ouroboros.Network.Diffusion.Configuration (ConsensusMode (..))
2627
import Ouroboros.Network.NodeToNode (AcceptedConnectionsLimit (..),
2728
DiffusionMode (InitiatorAndResponderDiffusionMode))
2829
import Ouroboros.Network.PeerSelection.PeerSharing (PeerSharing (..))
2930

31+
import Data.Default.Class
3032
import Data.Monoid (Last (..))
3133
import Data.Text (Text)
3234

3335
import Hedgehog (Property, discover, withTests, (===))
3436
import qualified Hedgehog
3537
import Hedgehog.Internal.Property (evalEither, failWith)
36-
import Ouroboros.Cardano.Network.Diffusion.Configuration (defaultNumberOfBigLedgerPeers)
3738

3839

3940
-- This is a simple test to check that the POM technique is working as intended.
@@ -181,7 +182,7 @@ testPartialCliConfig =
181182
, pncDatabaseFile = mempty
182183
, pncDiffusionMode = mempty
183184
, pncExperimentalProtocolsEnabled = Last $ Just True
184-
, pncProtocolFiles = Last . Just $ ProtocolFilepaths Nothing Nothing Nothing Nothing Nothing Nothing
185+
, pncProtocolFiles = Last . Just $ ProtocolFilepaths Nothing Nothing Nothing Nothing Nothing Nothing def
185186
, pncValidateDB = Last $ Just True
186187
, pncProtocolConfig = mempty
187188
, pncMaxConcurrencyBulkSync = mempty
@@ -227,7 +228,7 @@ eExpectedConfig = do
227228
, ncConfigFile = ConfigYamlFilePath "configuration/cardano/mainnet-config.json"
228229
, ncTopologyFile = TopologyFile "configuration/cardano/mainnet-topology.json"
229230
, ncDatabaseFile = OnePathForAllDbs "mainnet/db/"
230-
, ncProtocolFiles = ProtocolFilepaths Nothing Nothing Nothing Nothing Nothing Nothing
231+
, ncProtocolFiles = ProtocolFilepaths Nothing Nothing Nothing Nothing Nothing Nothing def
231232
, ncValidateDB = True
232233
, ncProtocolConfig = testNodeProtocolConfiguration
233234
, ncDiffusionMode = InitiatorAndResponderDiffusionMode

0 commit comments

Comments
 (0)