Não há nada no ambiente de processo convencional para tornar uma variável somente leitura, apenas os shells têm esse conceito para seu próprio ambiente / variáveis (os dois se sobrepõem de formas, mas devem ser entendidos como distintos) . O mesmo se aplica para as propriedades type como integer, array, function etc.
Você já pensou em usar um shell restrito, como rbash ?
Você pode definir rbash para executar readonly TEST_ENV ) em seu script de inicialização. rbash pode ser muito restritivo, mas aborda em grande parte as questões levantadas nos comentários.
O ambiente é uma parte de leitura / gravação de cada processo (veja aqui e aqui para detalhes), não apenas um processo pode gravar diretamente na parte de dados (via environ[] ), pode mudar os ponteiros para ele. Você pode fazer algo com isso (ou seja, mudar para uma página somente leitura) se for proficiente em montagem e puder encontrar o caminho em torno do código de inicialização crt , mas não o recomendo; -)
Se houver comandos específicos que os usuários executam que devem ter um determinado ambiente, você poderá definir somente a execução de grupo e usar sudo para definir as variáveis corretas (por exemplo, via /etc/environment ou env_file diretiva) e execute o comando como o grupo específico (preservando o uid). As versões recentes suportam noexec na maioria das plataformas, o que pode ser usado para evitar a execução de um novo shell (embora possa quebrar programas que legitimamente fork() e exec() ).
Como última opção, você pode usar LD_PRELOAD e sua própria biblioteca para definir um ambiente controlado para alguns / todos os processos do usuário, agrupando as funções libc relevantes ( getenv() putenv() etc.). O libfaketime faz exatamente isso para funções libc relacionadas ao tempo. Existe uma ferramenta menos conhecida chamada timetravel que faz semelhante, mas também ganchos setenv() e getenv() para manter o controle de LD_PRELOAD em face da adversidade.
A abordagem mais robusta é filtrar as chamadas setenv() unsetenv() putenv() (e possivelmente clearnv() ) para impedir a modificação e filtrar getenv() para que sempre retorne o valor necessário (possivelmente de um arquivo). Essa abordagem não impedirá a manipulação direta da memória do ambiente via environ[] , mas contanto que você esteja no controle de getenv() , qualquer processo que dependa da API libc deve ver seu valor. bash usa environ[] diretamente quando inicializa, mas também chama getenv() .