| 1 | package config |
| 2 | |
| 3 | import ( |
| 4 | "crypto/rand" |
| 5 | "encoding/base64" |
| 6 | "fmt" |
| 7 | "io" |
| 8 | "time" |
| 9 | |
| 10 | "github.com/cockroachdb/pebble/v2" |
| 11 | "github.com/ipfs/kubo/core/coreiface/options" |
| 12 | "github.com/libp2p/go-libp2p/core/crypto" |
| 13 | "github.com/libp2p/go-libp2p/core/peer" |
| 14 | ) |
| 15 | |
| 16 | func Init(out io.Writer, nBitsForKeypair int) (*Config, error) { |
| 17 | identity, err := CreateIdentity(out, []options.KeyGenerateOption{options.Key.Size(nBitsForKeypair)}) |
| 18 | if err != nil { |
| 19 | return nil, err |
| 20 | } |
| 21 | |
| 22 | return InitWithIdentity(identity) |
| 23 | } |
| 24 | |
| 25 | func InitWithIdentity(identity Identity) (*Config, error) { |
| 26 | datastore := DefaultDatastoreConfig() |
| 27 | |
| 28 | conf := &Config{ |
| 29 | API: API{ |
| 30 | HTTPHeaders: map[string][]string{}, |
| 31 | }, |
| 32 | |
| 33 | // setup the node's default addresses. |
| 34 | // NOTE: two swarm listen addrs, one tcp, one utp. |
| 35 | Addresses: addressesConfig(), |
| 36 | |
| 37 | Datastore: datastore, |
| 38 | Bootstrap: []string{AutoPlaceholder}, |
| 39 | Identity: identity, |
| 40 | Discovery: Discovery{ |
| 41 | MDNS: MDNS{ |
| 42 | Enabled: true, |
| 43 | }, |
| 44 | }, |
| 45 | |
| 46 | // setup the node mount points. |
| 47 | Mounts: Mounts{ |
| 48 | IPFS: "/ipfs", |
| 49 | IPNS: "/ipns", |
| 50 | MFS: "/mfs", |
| 51 | }, |
| 52 | |
| 53 | Ipns: Ipns{ |
| 54 | ResolveCacheSize: 128, |
| 55 | DelegatedPublishers: []string{AutoPlaceholder}, |
| 56 | }, |
| 57 | |
| 58 | Gateway: Gateway{ |
| 59 | RootRedirect: "", |
| 60 | NoFetch: false, |
| 61 | HTTPHeaders: map[string][]string{}, |
| 62 | }, |
| 63 | Pinning: Pinning{ |
| 64 | RemoteServices: map[string]RemotePinningService{}, |
| 65 | }, |
| 66 | DNS: DNS{ |
| 67 | Resolvers: map[string]string{ |
| 68 | ".": AutoPlaceholder, |
| 69 | }, |
| 70 | }, |
| 71 | Routing: Routing{ |
| 72 | DelegatedRouters: []string{AutoPlaceholder}, |
| 73 | }, |
| 74 | } |
| 75 | |
| 76 | return conf, nil |
| 77 | } |
| 78 | |
| 79 | // DefaultConnMgrHighWater is the default value for the connection managers |
| 80 | // 'high water' mark. |
| 81 | const DefaultConnMgrHighWater = 96 |
| 82 | |
| 83 | // DefaultConnMgrLowWater is the default value for the connection managers 'low |
| 84 | // water' mark. |
| 85 | const DefaultConnMgrLowWater = 32 |
| 86 | |
| 87 | // DefaultConnMgrGracePeriod is the default value for the connection managers |
| 88 | // grace period. |
| 89 | const DefaultConnMgrGracePeriod = time.Second * 20 |
| 90 | |
| 91 | // DefaultConnMgrSilencePeriod controls how often the connection manager enforces the limits. |
| 92 | const DefaultConnMgrSilencePeriod = time.Second * 10 |
| 93 | |
| 94 | // DefaultConnMgrType is the default value for the connection managers |
| 95 | // type. |
| 96 | const DefaultConnMgrType = "basic" |
| 97 | |
| 98 | // DefaultResourceMgrMinInboundConns is a MAGIC number that probably a good |
| 99 | // enough number of inbound conns to be a good network citizen. |
| 100 | const DefaultResourceMgrMinInboundConns = 800 |
| 101 | |
| 102 | func addressesConfig() Addresses { |
| 103 | return Addresses{ |
| 104 | Swarm: []string{ |
| 105 | "/ip4/0.0.0.0/tcp/4001", |
| 106 | "/ip6/::/tcp/4001", |
| 107 | "/ip4/0.0.0.0/udp/4001/webrtc-direct", |
| 108 | "/ip4/0.0.0.0/udp/4001/quic-v1", |
| 109 | "/ip4/0.0.0.0/udp/4001/quic-v1/webtransport", |
| 110 | "/ip6/::/udp/4001/webrtc-direct", |
| 111 | "/ip6/::/udp/4001/quic-v1", |
| 112 | "/ip6/::/udp/4001/quic-v1/webtransport", |
| 113 | }, |
| 114 | Announce: []string{}, |
| 115 | AppendAnnounce: []string{}, |
| 116 | NoAnnounce: []string{}, |
| 117 | API: Strings{"/ip4/127.0.0.1/tcp/5001"}, |
| 118 | Gateway: Strings{"/ip4/127.0.0.1/tcp/8080"}, |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | // DefaultDatastoreConfig is an internal function exported to aid in testing. |
| 123 | func DefaultDatastoreConfig() Datastore { |
| 124 | return Datastore{ |
| 125 | StorageMax: "10GB", |
| 126 | StorageGCWatermark: 90, // 90% |
| 127 | GCPeriod: "1h", |
| 128 | BloomFilterSize: 0, |
| 129 | Spec: flatfsSpec(), |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | func pebbleSpec() map[string]any { |
| 134 | return map[string]any{ |
| 135 | "type": "pebbleds", |
| 136 | "prefix": "pebble.datastore", |
| 137 | "path": "pebbleds", |
| 138 | "formatMajorVersion": int(pebble.FormatNewest), |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | func pebbleSpecMeasure() map[string]any { |
| 143 | return map[string]any{ |
| 144 | "type": "measure", |
| 145 | "prefix": "pebble.datastore", |
| 146 | "child": map[string]any{ |
| 147 | "formatMajorVersion": int(pebble.FormatNewest), |
| 148 | "type": "pebbleds", |
| 149 | "path": "pebbleds", |
| 150 | }, |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | func badgerSpec() map[string]any { |
| 155 | return map[string]any{ |
| 156 | "type": "badgerds", |
| 157 | "prefix": "badger.datastore", |
| 158 | "path": "badgerds", |
| 159 | "syncWrites": false, |
| 160 | "truncate": true, |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | func badgerSpecMeasure() map[string]any { |
| 165 | return map[string]any{ |
| 166 | "type": "measure", |
| 167 | "prefix": "badger.datastore", |
| 168 | "child": map[string]any{ |
| 169 | "type": "badgerds", |
| 170 | "path": "badgerds", |
| 171 | "syncWrites": false, |
| 172 | "truncate": true, |
| 173 | }, |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | func flatfsSpec() map[string]any { |
| 178 | return map[string]any{ |
| 179 | "type": "mount", |
| 180 | "mounts": []any{ |
| 181 | map[string]any{ |
| 182 | "mountpoint": "/blocks", |
| 183 | "type": "flatfs", |
| 184 | "prefix": "flatfs.datastore", |
| 185 | "path": "blocks", |
| 186 | "sync": false, |
| 187 | "shardFunc": "/repo/flatfs/shard/v1/next-to-last/2", |
| 188 | }, |
| 189 | map[string]any{ |
| 190 | "mountpoint": "/", |
| 191 | "type": "levelds", |
| 192 | "prefix": "leveldb.datastore", |
| 193 | "path": "datastore", |
| 194 | "compression": "none", |
| 195 | }, |
| 196 | }, |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | func flatfsSpecMeasure() map[string]any { |
| 201 | return map[string]any{ |
| 202 | "type": "mount", |
| 203 | "mounts": []any{ |
| 204 | map[string]any{ |
| 205 | "mountpoint": "/blocks", |
| 206 | "type": "measure", |
| 207 | "prefix": "flatfs.datastore", |
| 208 | "child": map[string]any{ |
| 209 | "type": "flatfs", |
| 210 | "path": "blocks", |
| 211 | "sync": false, |
| 212 | "shardFunc": "/repo/flatfs/shard/v1/next-to-last/2", |
| 213 | }, |
| 214 | }, |
| 215 | map[string]any{ |
| 216 | "mountpoint": "/", |
| 217 | "type": "measure", |
| 218 | "prefix": "leveldb.datastore", |
| 219 | "child": map[string]any{ |
| 220 | "type": "levelds", |
| 221 | "path": "datastore", |
| 222 | "compression": "none", |
| 223 | }, |
| 224 | }, |
| 225 | }, |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | // CreateIdentity initializes a new identity. |
| 230 | func CreateIdentity(out io.Writer, opts []options.KeyGenerateOption) (Identity, error) { |
| 231 | // TODO guard higher up |
| 232 | ident := Identity{} |
| 233 | |
| 234 | settings, err := options.KeyGenerateOptions(opts...) |
| 235 | if err != nil { |
| 236 | return ident, err |
| 237 | } |
| 238 | |
| 239 | var sk crypto.PrivKey |
| 240 | var pk crypto.PubKey |
| 241 | |
| 242 | switch settings.Algorithm { |
| 243 | case "rsa": |
| 244 | if settings.Size == -1 { |
| 245 | settings.Size = options.DefaultRSALen |
| 246 | } |
| 247 | |
| 248 | fmt.Fprintf(out, "generating %d-bit RSA keypair...", settings.Size) |
| 249 | |
| 250 | priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, settings.Size) |
| 251 | if err != nil { |
| 252 | return ident, err |
| 253 | } |
| 254 | |
| 255 | sk = priv |
| 256 | pk = pub |
| 257 | case "ed25519": |
| 258 | if settings.Size != -1 { |
| 259 | return ident, fmt.Errorf("number of key bits does not apply when using ed25519 keys") |
| 260 | } |
| 261 | fmt.Fprintf(out, "generating ED25519 keypair...") |
| 262 | priv, pub, err := crypto.GenerateEd25519Key(rand.Reader) |
| 263 | if err != nil { |
| 264 | return ident, err |
| 265 | } |
| 266 | |
| 267 | sk = priv |
| 268 | pk = pub |
| 269 | default: |
| 270 | return ident, fmt.Errorf("unrecognized key type: %s", settings.Algorithm) |
| 271 | } |
| 272 | fmt.Fprintf(out, "done\n") |
| 273 | |
| 274 | // currently storing key unencrypted. in the future we need to encrypt it. |
| 275 | // TODO(security) |
| 276 | skbytes, err := crypto.MarshalPrivateKey(sk) |
| 277 | if err != nil { |
| 278 | return ident, err |
| 279 | } |
| 280 | ident.PrivKey = base64.StdEncoding.EncodeToString(skbytes) |
| 281 | |
| 282 | id, err := peer.IDFromPublicKey(pk) |
| 283 | if err != nil { |
| 284 | return ident, err |
| 285 | } |
| 286 | ident.PeerID = id.String() |
| 287 | fmt.Fprintf(out, "peer identity: %s\n", ident.PeerID) |
| 288 | return ident, nil |
| 289 | } |