Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a7b81ce
fix(hugegraph-dist): skip init-store when graph.load_from_local_confi…
bitflicker64 Jul 26, 2026
69ac25e
fix(hugegraph-dist): gate init-store on a dedicated init_store.enable…
bitflicker64 Jul 26, 2026
e698202
style(hugegraph-dist): avoid em dashes in InitStore comment
bitflicker64 Jul 26, 2026
a62f6ff
fix(hugegraph-dist): refuse auth with init-store skipped unless usePD…
bitflicker64 Jul 27, 2026
3cb4fda
fix(hugegraph-dist): make set_prop match the separators get_prop accepts
bitflicker64 Jul 27, 2026
99d7de8
fix(hugegraph-dist): fail non-zero on unusable skip config, rewrite c…
bitflicker64 Jul 28, 2026
0dd5a7a
fix(hugegraph-dist): keep auth enablement complete and limit the admi…
bitflicker64 Jul 28, 2026
090763a
fix(hugegraph-dist): self-review follow-ups on the init-store skip path
bitflicker64 Jul 28, 2026
4c9be3a
Merge remote-tracking branch 'hugegraph/master' into fix/no-init
bitflicker64 Jul 28, 2026
95bfe45
fix(hugegraph-dist): harden skipped init authentication
bitflicker64 Jul 29, 2026
4b298bf
fix(hugegraph-dist): fail closed on auth config errors
bitflicker64 Jul 29, 2026
906a3ef
fix(hugegraph-api): always close init auth graph
bitflicker64 Jul 29, 2026
1434969
fix(hugegraph-dist): address init-store review findings
bitflicker64 Jul 30, 2026
3e505a8
fix(hugegraph-dist): fail fast on init-store override writes
bitflicker64 Jul 30, 2026
da85e46
fix(server): harden auth bootstrap upgrades
imbajin Jul 31, 2026
ba6b7e7
refactor(hugegraph-dist): narrow init-store gate to what #3118 asks for
bitflicker64 Aug 1, 2026
5268091
style(hugegraph-dist): address review follow-ups on the init-store gate
bitflicker64 Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,29 @@ Configuration is injected via environment variables. The old `docker/configs/app
| `HG_SERVER_BACKEND` | Yes | — | `backend` in `hugegraph.properties` | Storage backend (e.g. `hstore`) |
| `HG_SERVER_PD_PEERS` | Yes | — | `pd.peers` | PD cluster addresses (e.g. `pd0:8686,pd1:8686,pd2:8686`) |
| `STORE_REST` | No | — | Used by `wait-partition.sh` | Store REST endpoint for partition verification (e.g. `store0:8520`) |
| `PASSWORD` | No | — | Enables auth mode | Optional authentication password |
| `PASSWORD` | No | — | Enables auth mode | Optional authentication password; ignored when `HG_SERVER_INIT_STORE_ENABLED` is `false` (see below) |
| `HG_SERVER_INIT_STORE_ENABLED` | No | `true` | `init_store.enabled` in `rest-server.properties` | Set `false` in PD/HStore deployments so init-store skips local backend and admin initialization |

> **The built-in authenticator with `HG_SERVER_INIT_STORE_ENABLED=false`
> requires `usePD=true` and an HStore-backed `auth.graph_store`, unless
> `auth.remote_url` delegates auth elsewhere.** With init-store skipped, the
> server creates the built-in admin in PD metadata, and only an HStore auth
> graph uses the PD-backed auth manager that can read that account. init-store
> exits non-zero when the combination is unusable, rather than leaving a server
> nobody can log in to. A custom `auth.authenticator` is exempt because it
> manages its own identities.
>
> With the variable set to `false`, the entrypoint deliberately never writes
> `docker/init_complete`, so a later re-enable is still able to initialize.
> Setting the property directly in a mounted `rest-server.properties` does not
> get that guard.
>
> **`PASSWORD` has no effect on that path.** init-store reads it from standard
> input, and a disabled one returns before doing so. The admin is created on
> the PD startup path from `auth.admin_pa`, which defaults to the public value
> `pa`, so set it explicitly in a mounted `rest-server.properties`. It applies
> only when the account is first created, so changing it later does not rotate
> an existing password.

**Deprecated aliases** (still work but log a warning):

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,17 @@ public class ServerOptions extends OptionHolder {
"./conf/graphs"
);

public static final ConfigOption<Boolean> INIT_STORE_ENABLED =
new ConfigOption<>(
"init_store.enabled",
"Whether init-store initializes the local backend stores " +
"and the built-in admin account. Set false in distributed " +
"deployments (PD/HStore) where the storage side already " +
"owns the metadata.",
disallowEmpty(),
true
);

public static final ConfigOption<Boolean> SERVER_START_IGNORE_SINGLE_GRAPH_ERROR =
new ConfigOption<>(
"server.start_ignore_single_graph_error",
Expand Down
33 changes: 32 additions & 1 deletion hugegraph-server/hugegraph-dist/docker/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set -euo pipefail
DOCKER_FOLDER="./docker"
INIT_FLAG_FILE="init_complete"
GRAPH_CONF="./conf/graphs/hugegraph.properties"
REST_SERVER_CONF="./conf/rest-server.properties"

mkdir -p "${DOCKER_FOLDER}"

Expand Down Expand Up @@ -55,6 +56,23 @@ migrate_env "PD_PEERS" "HG_SERVER_PD_PEERS"
[[ -n "${HG_SERVER_BACKEND:-}" ]] && set_prop "backend" "${HG_SERVER_BACKEND}" "${GRAPH_CONF}"
[[ -n "${HG_SERVER_PD_PEERS:-}" ]] && set_prop "pd.peers" "${HG_SERVER_PD_PEERS}" "${GRAPH_CONF}"

# Normalized once here and reused by the init-flag guard below. The accepted
# spellings are the ones HugeConfig accepts, case-insensitive: commons-lang 2.x
# BooleanUtils, reached through commons-configuration 1.x PropertyConverter.
# That set excludes 0 and 1, which commons-lang3 would have taken. Anything
# outside it is rejected now rather than touching the init flag for a value the
# server is going to refuse anyway.
INIT_STORE_ENABLED=$(printf '%s' "${HG_SERVER_INIT_STORE_ENABLED:-}" |
tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
case "${INIT_STORE_ENABLED}" in
"" | y | t | yes | on | true | n | f | no | off | false) ;;
*) log "ERROR: invalid HG_SERVER_INIT_STORE_ENABLED" \
"'${HG_SERVER_INIT_STORE_ENABLED}'"
exit 1 ;;
esac
[[ -n "${INIT_STORE_ENABLED}" ]] && \
set_prop "init_store.enabled" "${INIT_STORE_ENABLED}" "${REST_SERVER_CONF}"

# ── Build wait-storage env ─────────────────────────────────────────────
WAIT_ENV=()
[[ -n "${HG_SERVER_BACKEND:-}" ]] && WAIT_ENV+=("hugegraph.backend=${HG_SERVER_BACKEND}")
Expand All @@ -74,9 +92,22 @@ if [[ ! -f "${DOCKER_FOLDER}/${INIT_FLAG_FILE}" ]]; then
else
log "init hugegraph with auth mode"
./bin/enable-auth.sh
# init-store reads the password from stdin, and a disabled one returns
# before it gets there, so say plainly that PASSWORD is being dropped
case "${INIT_STORE_ENABLED}" in
n | f | no | off | false)
log "WARN: PASSWORD is ignored while init-store is disabled;" \
"the admin is created on the PD startup path from" \
"'auth.admin_pa', which defaults to the public value 'pa'" ;;
esac
echo "${PASSWORD}" | ./bin/init-store.sh
fi
touch "${DOCKER_FOLDER}/${INIT_FLAG_FILE}"
# A disabled init-store initialized nothing, so recording it as done would
# stop a later re-enable from initializing.
case "${INIT_STORE_ENABLED}" in
n | f | no | off | false) ;;
*) touch "${DOCKER_FOLDER}/${INIT_FLAG_FILE}" ;;
esac
else
log "HugeGraph initialization already done. Skipping re-init..."
fi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,9 @@ CP="$CP":$(find -L "${LIB}" -name '*.jar' \! -name 'hugegraph*' | sort | tr '\n'
CP="$CP":$(find -L "${PLUGINS}" -name '*.jar' | sort | tr '\n' ':')
$JAVA -cp $CP ${DEFAULT_JAVA_OPTIONS} \
org.apache.hugegraph.cmd.InitStore "${CONF}"/rest-server.properties
INIT_STORE_STATUS=$?
if [[ ${INIT_STORE_STATUS} -ne 0 ]]; then
exit "${INIT_STORE_STATUS}"
fi

echo "Initialization finished."
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,31 @@ public static void main(String[] args) throws Exception {

String restConf = args[0];

RegisterUtil.registerBackends();
RegisterUtil.registerPlugins();
// Server options alone can answer the gate below. Backend and plugin
// registration waits for the enabled path, because registerPlugins()
// runs every plugin's register() and propagates its failures.
RegisterUtil.registerServer();

HugeConfig restServerConfig = new HugeConfig(restConf);

/*
* PD/HStore deployments let the storage side own the metadata, so
* init-store has nothing to do; on Kubernetes it re-ran on every Server
* pod restart, since the entrypoint's flag file does not survive one.
* Skipping also skips creating the built-in admin, which only the PD
* startup path can replace, and only for a PD-backed HStore auth graph.
*/
if (!restServerConfig.get(ServerOptions.INIT_STORE_ENABLED)) {
Comment thread
bitflicker64 marked this conversation as resolved.
LOG.warn("Skipping init-store: '{}' is false in '{}'. Local " +
"backend and admin initialization are not performed.",
ServerOptions.INIT_STORE_ENABLED.name(), restConf);
checkAdminBootstrapReachable(restServerConfig, restConf);
return;
Comment thread
bitflicker64 marked this conversation as resolved.
Comment thread
bitflicker64 marked this conversation as resolved.
}

RegisterUtil.registerBackends();
RegisterUtil.registerPlugins();

PDAuthConfig.setAuthority(
ServiceConstant.SERVICE_NAME,
ServiceConstant.AUTHORITY);
Expand Down Expand Up @@ -83,6 +103,74 @@ public static void main(String[] args) throws Exception {
}
}

/**
* Skipping leaves the built-in admin to GraphManager.initAdminUserIfNeeded()
* on the PD startup path, which writes it to PD metadata. Only an HStore
* auth graph reads that metadata back, so every other local built-in-auth
* configuration would start a server nobody can log in to. Remote auth and
* custom authenticators keep their identities elsewhere and are exempt.
*/
private static void checkAdminBootstrapReachable(HugeConfig conf,
String restConf) {
if (!requiresLocalBuiltinAdmin(conf)) {
return;
}
if (!conf.get(ServerOptions.USE_PD)) {
throw unreachableAdmin(restConf, ServerOptions.USE_PD.name() +
" is false");
}

String name = conf.get(ServerOptions.AUTH_GRAPH_STORE);
String path = ConfigUtil.scanGraphsDir(
conf.get(ServerOptions.GRAPHS)).get(name);
if (path == null) {
throw unreachableAdmin(restConf, "auth graph '" + name +
"' has no local configuration");
}
String backend = new HugeConfig(path).get(CoreOptions.BACKEND);
if (!"hstore".equals(backend)) {
throw unreachableAdmin(restConf, "auth graph '" + name +
"' uses backend '" + backend +
"', not 'hstore'");
}
}

private static IllegalStateException unreachableAdmin(String restConf,
String reason) {
return new IllegalStateException(String.format(
"Refusing to skip init-store: '%s' configures the built-in " +
"authenticator but %s, so the admin created on the PD startup " +
"path would be unreachable. See docker/README.md.",
restConf, reason));
}

/**
* HugeAuthenticator.loadAuthenticator() accepts any implementation class,
* and only StandardAuthenticator bootstraps HugeGraph's built-in admin
* account. A custom one (LDAP, OIDC, a plugin) manages its identities
* elsewhere, so it must not be held to the requirement above. The class is
* resolved without initializing it, and one that is not on the init-store
* classpath is by definition not the built-in authenticator.
*/
private static boolean requiresLocalBuiltinAdmin(HugeConfig conf) {
if (!conf.get(ServerOptions.AUTH_REMOTE_URL).isEmpty()) {
return false;
}
String authClass = conf.get(ServerOptions.AUTHENTICATOR);
if (authClass.isEmpty()) {
return false;
}
try {
Class<?> clazz = Class.forName(authClass, false,
InitStore.class.getClassLoader());
return StandardAuthenticator.class.isAssignableFrom(clazz);
} catch (ClassNotFoundException | LinkageError e) {
LOG.info("Authenticator '{}' is not on the init-store classpath, " +
"so it is not the built-in one", authClass);
return false;
}
}

private static HugeGraph initGraph(String configPath) throws Exception {
LOG.info("Init graph with config file: {}", configPath);
HugeConfig config = new HugeConfig(configPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.apache.hugegraph.unit.cache.CachedGraphTransactionTest;
import org.apache.hugegraph.unit.cache.CachedSchemaTransactionTest;
import org.apache.hugegraph.unit.cache.RamTableTest;
import org.apache.hugegraph.unit.cmd.InitStoreConfigTest;
import org.apache.hugegraph.unit.core.AnalyzerTest;
import org.apache.hugegraph.unit.core.BackendMutationTest;
import org.apache.hugegraph.unit.core.BackendStoreInfoTest;
Expand Down Expand Up @@ -155,6 +156,9 @@
HugeGraphAuthProxyTest.class,
SchemaElementTest.class,

/* cmd */
InitStoreConfigTest.class,

/* serializer */
BytesBufferTest.class,
SerializerFactoryTest.class,
Expand Down
Loading
Loading