[Intentional] Secrets are not visible in forks #14793

Open
opened 2025-11-02 11:23:06 -06:00 by GiteaMirror · 12 comments
Owner

Originally created by @n3o77 on GitHub (Jul 22, 2025).

Description

It seems that secret variables are not properly passed to environment variables in actions. But i'm not 100% sure what's going wrong as I can't echo the secrets which makes debugging hard.

The secret is properly set in the repository where the workflow is running. I'm using the same secret in another workflow, in the same repository, passing it through docker/build-push-action and it's working there without any issues so i'm confident that the secret is properly defined.

i.E.:

.gitea/workflows/ci.yml

on:
  pull_request:

name: "Continuous Integration"

jobs:
  coding-standards:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        operating-system: [ubuntu-latest]
        php-versions: ['8.3']

    name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }}

    env:
      extensions: mbstring
      key: cache-v1

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-versions }}
          extensions: ${{ env.extensions }}

      - name: "Install locked dependencies with composer"
        run: composer install --no-interaction --no-progress
        env:
          COMPOSER_AUTH: ${{ secrets.GT_COMPOSER_AUTH }}

With this no authentication is used. And the install failes.

When i'm trying with this:

...
        env:
           COMPOSER_AUTH: '{"http-basic": {"gitea_url": {"username": "myusername", "password": "${{ secrets.GT_TOKEN }}"}}}'
...

Then authentication is used with myusername but fails because the password is empty. If i'm setting the password instead of using the secret everything works fine.

Gitea Version

1.24.3

Can you reproduce the bug on the Gitea demo site?

No

Log Gist

No response

Screenshots

No response

Git Version

No response

Operating System

No response

How are you running Gitea?

Docker Image

Database

MySQL/MariaDB

Originally created by @n3o77 on GitHub (Jul 22, 2025). ### Description It seems that secret variables are not properly passed to environment variables in actions. But i'm not 100% sure what's going wrong as I can't echo the secrets which makes debugging hard. The secret is properly set in the repository where the workflow is running. I'm using the same secret in another workflow, in the same repository, passing it through `docker/build-push-action` and it's working there without any issues so i'm confident that the secret is properly defined. i.E.: .gitea/workflows/ci.yml ``` on: pull_request: name: "Continuous Integration" jobs: coding-standards: runs-on: ubuntu-latest strategy: matrix: operating-system: [ubuntu-latest] php-versions: ['8.3'] name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} env: extensions: mbstring key: cache-v1 steps: - name: Checkout uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} extensions: ${{ env.extensions }} - name: "Install locked dependencies with composer" run: composer install --no-interaction --no-progress env: COMPOSER_AUTH: ${{ secrets.GT_COMPOSER_AUTH }} ``` With this no authentication is used. And the install failes. When i'm trying with this: ``` ... env: COMPOSER_AUTH: '{"http-basic": {"gitea_url": {"username": "myusername", "password": "${{ secrets.GT_TOKEN }}"}}}' ... ``` Then authentication is used with `myusername` but fails because the password is empty. If i'm setting the password instead of using the secret everything works fine. ### Gitea Version 1.24.3 ### Can you reproduce the bug on the Gitea demo site? No ### Log Gist _No response_ ### Screenshots _No response_ ### Git Version _No response_ ### Operating System _No response_ ### How are you running Gitea? Docker Image ### Database MySQL/MariaDB
GiteaMirror added the issue/not-a-bug label 2025-11-02 11:23:06 -06:00
Author
Owner

@n3o77 commented on GitHub (Jul 22, 2025):

I did some more testing. This seems to be only a problem on a PR from a fork. If i create a PR from another branch in the main repository everything works as expected.

@n3o77 commented on GitHub (Jul 22, 2025): I did some more testing. This seems to be only a problem on a PR from a fork. If i create a PR from another branch in the main repository everything works as expected.
Author
Owner

@delvh commented on GitHub (Jul 22, 2025):

Ah, but now we are entering dangerous territory.
IIRC, that is intended:
Otherwise, a malicious fork can simply steal your secrets which are often things such as access keys or similar.
As it should be right now, secrets do not cross repo boundaries.
The only people allowed to see the secrets are the owner and/or admins of the repo itself.
In other words: Even in the best case, it would allow forks to impersonate the real project.
That's why forks don't receive the secrets of the parent.

@delvh commented on GitHub (Jul 22, 2025): Ah, but now we are entering dangerous territory. IIRC, that is intended: Otherwise, a malicious fork can simply steal your secrets which are often things such as access keys or similar. As it should be right now, secrets do not cross repo boundaries. The only people allowed to see the secrets are the owner and/or admins of the repo itself. In other words: Even in the best case, it would allow forks to impersonate the real project. That's why forks don't receive the secrets of the parent.
Author
Owner

@n3o77 commented on GitHub (Jul 22, 2025):

I see, that makes some sense. Some kind of notice / error would be nice as I wasted quite some hours because I thought something is wrong with my setup. It would also be nice that we could either configure secrets on the forked repositories which can then be used or maybe workflows which are already in the main branch.

In our usecase we want to run tests before a PR can be merged, but to be able to run tests it's necessary to pull private repositories... i don't think that's uncommon, or is it?

@n3o77 commented on GitHub (Jul 22, 2025): I see, that makes some sense. Some kind of notice / error would be nice as I wasted quite some hours because I thought something is wrong with my setup. It would also be nice that we could either configure secrets on the forked repositories which can then be used or maybe workflows which are already in the main branch. In our usecase we want to run tests before a PR can be merged, but to be able to run tests it's necessary to pull private repositories... i don't think that's uncommon, or is it?
Author
Owner

@delvh commented on GitHub (Jul 22, 2025):

No, it's not uncommon.
Nevertheless, each of the private forks will need to configure the necessary secrets for itself again.
(At least while there's no better option to distinguish "friendly" forks from untrusted forks)

But yeah, I agree, there should be some kind of warning somewhere.
To me, the best place sounds like a mixture of both

  1. the workflow itself when viewing it from the Gitea UI
  2. A warning log inside the workflow if the injected value does not exist.

@ChristopherHX regarding 2:
Do you agree that a warning like that would make sense when a value cannot be injected?
If yes, should that happen in act? Or where would that belong? Into the runner? Gitea itself?
and if we were to implement 1, where would we get the information from? Would that require any change outside of Gitea?

@delvh commented on GitHub (Jul 22, 2025): No, it's not uncommon. Nevertheless, each of the private forks will need to configure the necessary secrets for itself again. (At least while there's no better option to distinguish "friendly" forks from untrusted forks) But yeah, I agree, there should be some kind of warning somewhere. To me, the best place sounds like a mixture of both 1) the workflow itself when viewing it from the Gitea UI 2) A warning log inside the workflow if the injected value does not exist. @ChristopherHX regarding 2: Do you agree that a warning like that would make sense when a value cannot be injected? If yes, should that happen in `act`? Or where would that belong? Into the runner? Gitea itself? and if we were to implement 1, where would we get the information from? Would that require any change outside of Gitea?
Author
Owner

@n3o77 commented on GitHub (Jul 22, 2025):

Sounds good. Thank you very much.
Can you think of any workaround for now to get that working? For us it's only used internally with trusted employees, so not really an issue with external PRs.

Another thing which would be nice to display is which secret was used (project secret, orga. secret or user secret) or if the secret doesn't exist at all. That would make debugging much easier.

@n3o77 commented on GitHub (Jul 22, 2025): Sounds good. Thank you very much. Can you think of any workaround for now to get that working? For us it's only used internally with trusted employees, so not really an issue with external PRs. Another thing which would be nice to display is which secret was used (project secret, orga. secret or user secret) or if the secret doesn't exist at all. That would make debugging much easier.
Author
Owner

@delvh commented on GitHub (Jul 22, 2025):

Can you think of any workaround for now to get that working?

Copy the necessary secrets for each employee individually into their repo.

@delvh commented on GitHub (Jul 22, 2025): > Can you think of any workaround for now to get that working? Copy the necessary secrets for each employee individually into their repo.
Author
Owner

@n3o77 commented on GitHub (Jul 22, 2025):

Where? In the forked repo settings there's no setting for actions / secrets.
I tried the user settings -> secrets, but that doesn't seem to work either.

@n3o77 commented on GitHub (Jul 22, 2025): Where? In the forked repo settings there's no setting for actions / secrets. I tried the user settings -> secrets, but that doesn't seem to work either.
Author
Owner

@lunny commented on GitHub (Jul 22, 2025):

If you’re in a trusted environment, ask your employee to create a pull request directly from the main repository. That should work.

@lunny commented on GitHub (Jul 22, 2025): If you’re in a trusted environment, ask your employee to create a pull request directly from the main repository. That should work.
Author
Owner

@delvh commented on GitHub (Jul 22, 2025):

Regarding the no secrets in settings:
Please enable actions first for the fork:

Image
@delvh commented on GitHub (Jul 22, 2025): Regarding the `no secrets in settings`: Please enable actions first for the fork: <img width="687" height="296" alt="Image" src="https://github.com/user-attachments/assets/c6bb26f1-492d-4d98-9cd0-8b2c8ea9e066" />
Author
Owner

@n3o77 commented on GitHub (Jul 22, 2025):

Unfortunately that doesn't seem to work either for PRs to the main repo. But it works if I create a PR inside the fork. So i guess the only way right now is creating branches in the main repository and then create PRs from there or some variant of that. Not great but better than nothing i guess.

Again, thank you @delvh .

@n3o77 commented on GitHub (Jul 22, 2025): Unfortunately that doesn't seem to work either for PRs to the main repo. But it works if I create a PR inside the fork. So i guess the only way right now is creating branches in the main repository and then create PRs from there or some variant of that. Not great but better than nothing i guess. Again, thank you @delvh .
Author
Owner

@ChristopherHX commented on GitHub (Jul 22, 2025):

You could dangerously switch to pull_request_target, then the workflow file is taken from the base branch not from the fork.

on:
  pull_request_target:

name: "Continuous Integration"

jobs:
  coding-standards:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        operating-system: [ubuntu-latest]
        php-versions: ['8.3']

    name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }}

    env:
      extensions: mbstring
      key: cache-v1

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          # Dangerous, do this only on internal / private instances / repositories or if the secret is not critical
          ref: refs/pull/${{ github.event.pull_request.number }}/head

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-versions }}
          extensions: ${{ env.extensions }}

      - name: "Install locked dependencies with composer"
        run: composer install --no-interaction --no-progress
        env:
          COMPOSER_AUTH: ${{ secrets.GT_COMPOSER_AUTH }}

The GitHub Actions Setting, I do not care about this protection and I want that forks see secrets for inner source is not implemented

The only people allowed to see the secrets are the owner and/or admins of the repo itself.

Secrets are far less protected, write access to repository = secret access if you can trigger a workflow.

The hardening mechanism "environment secrets" coupled with branch protection or manual approval are not implemented in Gitea Actions.

Do you agree that a warning like that would make sense when a value cannot be injected?

Yes this makes sense to me. Would certainly help a lot for people to save time.

if we were to implement 1, where would we get the information from? Would that require any change outside of Gitea?

Generally implementing this only requires actionlint and yaml.v3 as external dependencies that are already used, e.g. taking my schema validator and traversing over every expression and look for secrets and vars usages if we happen to have an pull_request event instead if doing schema validation.

In this case using acts data structures makes no sense, maybe customize and embed this directly into Gitea? (It's 100% my own code and has not landed in gitea/act).

where would we get the information from?

By reading the workflow and parsing the expressions

Or where would that belong? Into the runner? Gitea itself?

This depends on architecture, something like this has no dependency to act. So I would put this into Gitea.

I wonder:
Do we want to see an warning if the PR is not from a fork? Sometimes people with write access create a workflow, test it and then wonder why it not works for forks.

  1. the workflow itself when viewing it from the Gitea UI

I would prefer this, e.g. you know this before the job starts.

However

  1. warning log inside the workflow if the injected value does not exist.

Option 2 could work for the barely supported reusable workflows as well (Which I want to move to Gitea Server side, except if you specify runs-on for the workflow)...

@ChristopherHX commented on GitHub (Jul 22, 2025): <Details> <Summary> You could **dangerously** switch to `pull_request_target`, then the workflow file is taken from the **base** branch not from the fork. </Summary> ```yaml on: pull_request_target: name: "Continuous Integration" jobs: coding-standards: runs-on: ubuntu-latest strategy: matrix: operating-system: [ubuntu-latest] php-versions: ['8.3'] name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} env: extensions: mbstring key: cache-v1 steps: - name: Checkout uses: actions/checkout@v4 with: # Dangerous, do this only on internal / private instances / repositories or if the secret is not critical ref: refs/pull/${{ github.event.pull_request.number }}/head - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} extensions: ${{ env.extensions }} - name: "Install locked dependencies with composer" run: composer install --no-interaction --no-progress env: COMPOSER_AUTH: ${{ secrets.GT_COMPOSER_AUTH }} ``` </Details> _The GitHub Actions Setting, I do not care about this protection and I want that forks see secrets for **inner source** is not implemented_ > The only people allowed to see the secrets are the owner and/or admins of the repo itself. Secrets are far less protected, write access to repository = secret access if you can trigger a workflow. The hardening mechanism "environment secrets" coupled with **branch protection** or **manual approval** are not implemented in Gitea Actions. > Do you agree that a warning like that would make sense when a value cannot be injected? Yes this makes sense to me. Would certainly help a lot for people to save time. > if we were to implement 1, where would we get the information from? Would that require any change outside of Gitea? Generally implementing this only requires actionlint and yaml.v3 as external dependencies that are already used, e.g. taking [my schema validator](https://github.com/nektos/act/blob/master/pkg/schema/schema.go) and traversing over every expression and look for `secrets` and `vars` usages if we happen to have an `pull_request` event instead if doing schema validation. In this case using acts data structures makes no sense, maybe customize and embed this directly into Gitea? (It's 100% my own code and has not landed in gitea/act). > where would we get the information from? By reading the workflow and parsing the expressions > Or where would that belong? Into the runner? Gitea itself? This depends on architecture, something like this has no dependency to act. So I would put this into Gitea. I wonder: Do we want to see an warning if the PR is not from a fork? Sometimes people with write access create a workflow, test it and then wonder why it not works for forks. > 1. the workflow itself when viewing it from the Gitea UI I would prefer this, e.g. you know this before the job starts. However > 2. warning log inside the workflow if the injected value does not exist. Option 2 could work for the barely supported reusable workflows as well (Which I want to move to Gitea Server side, except if you specify runs-on for the workflow)...
Author
Owner

@n3o77 commented on GitHub (Jul 22, 2025):

@ChristopherHX that works for me and in this particular case is fine security wise. Thank you very much!

I think using the secrets from the fork for those PRs would be optimal, then every dev can manage their own secrets. Any chance to get that implemented at some point?

@n3o77 commented on GitHub (Jul 22, 2025): @ChristopherHX that works for me and in this particular case is fine security wise. Thank you very much! I think using the secrets from the fork for those PRs would be optimal, then every dev can manage their own secrets. Any chance to get that implemented at some point?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/gitea#14793