-
Notifications
You must be signed in to change notification settings - Fork 178
appsec: fix bailouts during rshutdown #3941
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
cataphract
wants to merge
2
commits into
glopes/request-exec-outside-req
Choose a base branch
from
glopes/rshutdown-bailout
base: glopes/request-exec-outside-req
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/LogFile.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| package com.datadog.appsec.php.docker | ||
|
|
||
| /** | ||
| * A log file inside an {@link AppSecContainer}. Supports marking the current | ||
| * end-of-file position and then reading only what was appended since the mark, | ||
| * which is the usual way a test isolates the log output of a single action. | ||
| * | ||
| * <pre> | ||
| * def lf = new LogFile(container, 'helper.log') | ||
| * lf.markEndPos() | ||
| * ... do something that writes to the log ... | ||
| * lf.linesSinceMark().any { it.contains('boom') } | ||
| * </pre> | ||
| * | ||
| * A {@code name} without a leading {@code /} is resolved under {@link #LOG_DIR}. | ||
| */ | ||
| class LogFile { | ||
| static final String LOG_DIR = '/tmp/logs' | ||
|
|
||
| private final AppSecContainer container | ||
| final String path | ||
| private long markPos = 0 | ||
|
|
||
| LogFile(AppSecContainer container, String name) { | ||
| this.container = container | ||
| this.path = name.startsWith('/') ? name : "${LOG_DIR}/${name}" | ||
| } | ||
|
|
||
| /** Current size of the log in bytes (0 if it does not exist yet). */ | ||
| long size() { | ||
| container.execInContainer('bash', '-c', "wc -c < ${path} 2>/dev/null || echo 0".toString()) | ||
| .stdout.trim() as long | ||
| } | ||
|
|
||
| /** Record the current end position; subsequent reads start from here. */ | ||
| void markEndPos() { | ||
| markPos = size() | ||
| } | ||
|
|
||
| /** Raw text appended since the last {@link #markEndPos()}. */ | ||
| String getTextSinceMark() { | ||
| container.execInContainer('bash', '-c', "tail -c +${markPos + 1} ${path}".toString()).stdout | ||
| } | ||
|
|
||
| /** Lines appended since the last {@link #markEndPos()}. */ | ||
| List<String> getLinesSinceMark() { | ||
| getTextSinceMark().readLines() | ||
| } | ||
| } |
97 changes: 97 additions & 0 deletions
97
appsec/tests/integration/src/main/groovy/com/datadog/appsec/php/docker/PhpFpm.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| package com.datadog.appsec.php.docker | ||
|
|
||
| import groovy.util.logging.Slf4j | ||
| import org.testcontainers.containers.Container.ExecResult | ||
|
|
||
| /** | ||
| * Helpers for reconfiguring PHP-FPM inside a running {@link AppSecContainer}. | ||
| * | ||
| * Two strategies are offered: | ||
| * <ul> | ||
| * <li>{@link #restart} hard-kills every php-fpm process and relaunches the | ||
| * master, optionally exporting extra environment variables or loading a | ||
| * different php.ini. Use it for changes that only take effect on a cold | ||
| * start (environment variables, php.ini edits).</li> | ||
| * <li>{@link #reload} sends SIGUSR2 for a graceful reload and blocks until the | ||
| * pre-reload workers have been replaced. Use it for pool.d settings (e.g. | ||
| * {@code pm.max_children}) that FPM re-reads on USR2; pair it with | ||
| * {@link #setPoolValue} / {@link #backupPoolConfig} / {@link #restorePoolConfig}.</li> | ||
| * </ul> | ||
| */ | ||
| @Slf4j | ||
| class PhpFpm { | ||
| static final String FPM_CONF = '/etc/php-fpm.conf' | ||
| static final String DEFAULT_INI = '/etc/php/php.ini' | ||
| static final String POOL_CONF = '/etc/php-fpm.d/www.conf' | ||
|
|
||
| private final AppSecContainer container | ||
|
|
||
| PhpFpm(AppSecContainer container) { | ||
| this.container = container | ||
| } | ||
|
|
||
| /** | ||
| * Hard-restart php-fpm: kill every php-fpm process and relaunch the master. | ||
| * | ||
| * @param env extra environment variables exported before launch | ||
| * @param iniPath the php.ini to load (defaults to {@link #DEFAULT_INI}) | ||
| */ | ||
| ExecResult restart(Map<String, String> env = [:], String iniPath = DEFAULT_INI) { | ||
| container.flushProfilingData() | ||
| String exports = env.collect { k, v -> "export ${k}=${v};" }.join(' ') | ||
| ExecResult res = container.execInContainer('bash', '-c', | ||
| "kill -9 `pgrep php-fpm`; ${exports} php-fpm -y ${FPM_CONF} -c ${iniPath}".toString()) | ||
| assert res.exitCode == 0 : "php-fpm restart failed: ${res.stderr}" | ||
| res | ||
| } | ||
|
|
||
| /** Rewrite a single pool directive (e.g. {@code pm.max_children}) in place. */ | ||
| void setPoolValue(String key, String value, String poolConf = POOL_CONF) { | ||
| container.execInContainer('sed', '-i', "s/${key} = .*/${key} = ${value}/".toString(), poolConf) | ||
| } | ||
|
|
||
| /** Back up the pool config so {@link #restorePoolConfig} can revert any edits. */ | ||
| void backupPoolConfig(String poolConf = POOL_CONF) { | ||
| container.execInContainer('cp', poolConf, "${poolConf}.bak".toString()) | ||
| } | ||
|
|
||
| /** Restore the pool config saved by {@link #backupPoolConfig}. */ | ||
| void restorePoolConfig(String poolConf = POOL_CONF) { | ||
| container.execInContainer('mv', "${poolConf}.bak".toString(), poolConf) | ||
| } | ||
|
|
||
| /** | ||
| * Gracefully reload the FPM master (re-reads pool config without dropping the | ||
| * socket) and block until every pre-reload worker has been replaced by a | ||
| * freshly-spawned one. | ||
| */ | ||
| void reload(long timeoutMillis = 5_000) { | ||
| List<String> old = workerPids() | ||
| // Locate the master via its pid file, falling back to the oldest php-fpm process. | ||
| container.execInContainer('bash', '-c', | ||
| 'kill -USR2 $(cat /run/php-fpm*.pid /var/run/php-fpm*.pid 2>/dev/null | head -1) ' + | ||
| '2>/dev/null || pkill -USR2 -o php-fpm || true') | ||
| waitForWorkerTurnover(old, timeoutMillis) | ||
| } | ||
|
|
||
| /** PIDs of the current pool worker processes (the master is excluded). */ | ||
| List<String> workerPids() { | ||
| container.execInContainer('bash', '-c', "pgrep -f 'php-fpm: pool' || true") | ||
| .stdout.readLines()*.trim().findAll { it } | ||
| } | ||
|
|
||
| private void waitForWorkerTurnover(List<String> old, long timeoutMillis) { | ||
| long deadline = System.currentTimeMillis() + timeoutMillis | ||
| while (true) { | ||
| List<String> current = workerPids() | ||
| if (!current.isEmpty() && current.intersect(old).isEmpty()) { | ||
| return | ||
| } | ||
| if (System.currentTimeMillis() > deadline) { | ||
| throw new IllegalStateException( | ||
| "php-fpm workers were not reloaded in time (old=${old}, current=${current})".toString()) | ||
| } | ||
| Thread.sleep(100) | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These new imports reference
com.datadog.appsec.php.docker.LogFileandPhpFpm, but the commit does not add either class; a repo-widergshows the docker test package only contains the existing helpers (AppSecContainer,FailOnUnmatchedTraces,InspectContainerHelper, etc.). As a result, the appsec integration test sources that now import/use these helpers (NginxFpmTests,RoadRunnerTests,ZtsGshutdownTests, and the FPM sampling traits/classes) will fail to compile before any tests run.Useful? React with 👍 / 👎.